diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index ecb8426eb..b6f985fb6 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -30,7 +30,6 @@ jobs: dash_deserialize_address, dash_script_bytes_to_asm_fmt, # dash_deserialize_prefilled_transaction, - dash_deserialize_witness, # dash_deserialize_psbt, dash_outpoint_string, dash_deserialize_script, diff --git a/dash-spv/src/network/message_type.rs b/dash-spv/src/network/message_type.rs index 9d2dfb86d..edbccca0f 100644 --- a/dash-spv/src/network/message_type.rs +++ b/dash-spv/src/network/message_type.rs @@ -110,8 +110,6 @@ define_message_types! { Reject (..), /// `feefilter` FeeFilter (..), - /// `wtxidrelay` - WtxidRelay, /// `addrv2` AddrV2 (..), /// `sendaddrv2` diff --git a/dash/Cargo.toml b/dash/Cargo.toml index 0bcaf4203..7a42a688f 100644 --- a/dash/Cargo.toml +++ b/dash/Cargo.toml @@ -87,14 +87,6 @@ key-wallet = { path = "../key-wallet" } name = "handshake" required-features = ["std"] -[[example]] -name = "ecdsa-psbt" -required-features = ["std", "bitcoinconsensus"] - -[[example]] -name = "taproot-psbt" -required-features = ["std", "rand-std", "bitcoinconsensus"] - [[bench]] name = "transaction" harness = false diff --git a/dash/examples/ecdsa-psbt.rs b/dash/examples/ecdsa-psbt.rs deleted file mode 100644 index 313675537..000000000 --- a/dash/examples/ecdsa-psbt.rs +++ /dev/null @@ -1,320 +0,0 @@ -//! Implements an example PSBT workflow. -//! -//! The workflow we simulate is that of a setup using a watch-only online wallet (contains only -//! public keys) and a cold-storage signing wallet (contains the private keys). -//! -//! You can verify the workflow using `dashd` and `dash-cli`. -//! -//! ## Example Setup -//! -//! 1. Start dash Core in Regtest mode, for example: -//! -//! `dashd -regtest -server -daemon -fallbackfee=0.0002 -rpcuser=admin -rpcpassword=pass -rpcallowip=127.0.0.1/0 -rpcbind=127.0.0.1 -blockfilterindex=1 -peerblockfilters=1` -//! -//! 2. Define a shell alias to `dash-cli`, for example: -//! -//! `alias bt=dash-cli -rpcuser=admin -rpcpassword=pass -rpcport=18443` -//! -//! 3. Create (or load) a default wallet, for example: -//! -//! `bt createwallet ` -//! -//! 4. Mine some blocks, for example: -//! -//! `bt generatetoaddress 110 $(bt getnewaddress)` -//! -//! 5. Get the details for a UTXO to fund the PSBT with: -//! -//! `bt listunspent` -//! - -use std::boxed::Box; -use std::collections::BTreeMap; -use std::fmt; -use std::str::FromStr; - -use dashcore::consensus::encode; -use dashcore::secp256k1::{Secp256k1, Signing, Verification}; -use dashcore::{ - Address, Amount, Network, OutPoint, PublicKey, ScriptBuf, Transaction, TxIn, TxOut, Witness, -}; -use key_wallet::bip32::{ - ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivationPath, -}; -use key_wallet::psbt::{self, Input, Psbt, PsbtSighashType}; - -type Result = std::result::Result; - -// Get this from the output of `bt dumpwallet `. -const EXTENDED_MASTER_PRIVATE_KEY: &str = "tprv8ZgxMBicQKsPeSHZFZWT8zxie2dXWcwemnTkf4grVzMvP2UABUxqbPTCHzZ4ztwhBghpfFw27sJqEgW6y1ZTZcfvCUdtXE1L6qMF7TBdbqQ"; - -// Set these with valid data from output of step 5 above. Please note, input utxo must be a p2wpkh. -const INPUT_UTXO_TXID: &str = "295f06639cde6039bf0c3dbf4827f0e3f2b2c2b476408e2f9af731a8d7a9c7fb"; -const INPUT_UTXO_VOUT: u32 = 0; -const INPUT_UTXO_SCRIPT_PUBKEY: &str = "00149891eeb8891b3e80a2a1ade180f143add23bf5de"; -const INPUT_UTXO_VALUE: &str = "50 BTC"; -// Get this from the descriptor, -// "wpkh([97f17dca/0'/0'/0']02749483607dafb30c66bd93ece4474be65745ce538c2d70e8e246f17e7a4e0c0c)#m9n56cx0". -const INPUT_UTXO_DERIVATION_PATH: &str = "m/0h/0h/0h"; - -// Grab an address to receive on: `bt generatenewaddress` (obviously contrived but works as an example). -const RECEIVE_ADDRESS: &str = "bcrt1qcmnpjjjw78yhyjrxtql6lk7pzpujs3h244p7ae"; // The address to receive the coins we send. - -// These should be correct if the UTXO above should is for 50 BTC. -const OUTPUT_AMOUNT_BTC: &str = "1 BTC"; -const CHANGE_AMOUNT_BTC: &str = "48.99999 BTC"; // 1000 sat transaction fee. - -const NETWORK: Network = Network::Regtest; - -fn main() -> Result<()> { - let secp = Secp256k1::new(); - - let (offline, fingerprint, account_0_xpub, input_xpub) = - ColdStorage::new(&secp, EXTENDED_MASTER_PRIVATE_KEY)?; - - let online = WatchOnly::new(account_0_xpub, input_xpub, fingerprint); - - let created = online.create_psbt(&secp)?; - let updated = online.update_psbt(created)?; - - let signed = offline.sign_psbt(&secp, updated)?; - - let finalized = online.finalize_psbt(signed)?; - - // You can use `bt sendrawtransaction` to broadcast the extracted transaction. - let tx = finalized.extract_tx(); - tx.verify(|_| Some(previous_output())).expect("failed to verify transaction"); - - let hex = encode::serialize_hex(&tx); - println!("You should now be able to broadcast the following transaction: \n\n{}", hex); - - Ok(()) -} - -// We cache the pubkeys for convenience because it requires a scep context to convert the private key. -/// An example of an offline signer i.e., a cold-storage device. -struct ColdStorage { - /// The master extended private key. - master_xpriv: ExtendedPrivKey, - /// The master extended public key. - master_xpub: ExtendedPubKey, -} - -/// The data exported from an offline wallet to enable creation of a watch-only online wallet. -/// (wallet, fingerprint, account_0_xpub, input_utxo_xpub) -type ExportData = (ColdStorage, Fingerprint, ExtendedPubKey, ExtendedPubKey); - -impl ColdStorage { - /// Constructs a new `ColdStorage` signer. - /// - /// # Returns - /// - /// The newly created signer along with the data needed to configure a watch-only wallet. - fn new(secp: &Secp256k1, xpriv: &str) -> Result { - let master_xpriv = ExtendedPrivKey::from_str(xpriv)?; - let master_xpub = ExtendedPubKey::from_priv(secp, &master_xpriv); - - // Hardened children require secret data to derive. - - let path = "m/84h/0h/0h".into_derivation_path()?; - let account_0_xpriv = master_xpriv.derive_priv(secp, &path)?; - let account_0_xpub = ExtendedPubKey::from_priv(secp, &account_0_xpriv); - - let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?; - let input_xpriv = master_xpriv.derive_priv(secp, &path)?; - let input_xpub = ExtendedPubKey::from_priv(secp, &input_xpriv); - - let wallet = ColdStorage { - master_xpriv, - master_xpub, - }; - let fingerprint = wallet.master_fingerprint(); - - Ok((wallet, fingerprint, account_0_xpub, input_xpub)) - } - - /// Returns the fingerprint for the master extended public key. - fn master_fingerprint(&self) -> Fingerprint { - self.master_xpub.fingerprint() - } - - /// Signs `psbt` with this signer. - fn sign_psbt(&self, secp: &Secp256k1, mut psbt: Psbt) -> Result { - match psbt.sign(&self.master_xpriv, secp) { - Ok(keys) => assert_eq!(keys.len(), 1), - Err((_, e)) => { - let e = e.get(&0).expect("at least one error"); - return Err(e.clone().into()); - } - }; - Ok(psbt) - } -} - -/// An example of an watch-only online wallet. -struct WatchOnly { - /// The xpub for account 0 derived from derivation path "m/84h/0h/0h". - account_0_xpub: ExtendedPubKey, - /// The xpub derived from `INPUT_UTXO_DERIVATION_PATH`. - input_xpub: ExtendedPubKey, - /// The master extended pubkey fingerprint. - master_fingerprint: Fingerprint, -} - -impl WatchOnly { - /// Constructs a new watch-only wallet. - /// - /// A watch-only wallet would typically be online and connected to the dash network. We - /// 'import' into the wallet the `account_0_xpub` and `master_fingerprint`. - /// - /// The reason for importing the `input_xpub` is so one can use dashd to grab a valid input - /// to verify the workflow presented in this file. - fn new( - account_0_xpub: ExtendedPubKey, - input_xpub: ExtendedPubKey, - master_fingerprint: Fingerprint, - ) -> Self { - WatchOnly { - account_0_xpub, - input_xpub, - master_fingerprint, - } - } - - /// Creates the PSBT, in BIP174 parlance this is the 'Creator'. - fn create_psbt(&self, secp: &Secp256k1) -> Result { - let to_address = Address::from_str(RECEIVE_ADDRESS)?.require_network(Network::Regtest)?; - let to_amount = Amount::from_str(OUTPUT_AMOUNT_BTC)?; - - let (_, change_address, _) = self.change_address(secp)?; - let change_amount = Amount::from_str(CHANGE_AMOUNT_BTC)?; - - let tx = Transaction { - version: 2, - lock_time: 0, - input: vec![TxIn { - previous_output: OutPoint { - txid: INPUT_UTXO_TXID.parse()?, - vout: INPUT_UTXO_VOUT, - }, - script_sig: ScriptBuf::new(), - sequence: u32::MAX, // Disable LockTime and RBF. - witness: Witness::default(), - }], - output: vec![ - TxOut { - value: to_amount.to_sat(), - script_pubkey: to_address.script_pubkey(), - }, - TxOut { - value: change_amount.to_sat(), - script_pubkey: change_address.script_pubkey(), - }, - ], - special_transaction_payload: None, - }; - - let psbt = Psbt::from_unsigned_tx(tx)?; - - Ok(psbt) - } - - /// Updates the PSBT, in BIP174 parlance this is the 'Updater'. - fn update_psbt(&self, mut psbt: Psbt) -> Result { - let mut input = Input { - witness_utxo: Some(previous_output()), - ..Default::default() - }; - - let pk = self.input_xpub.to_pub(); - let wpkh = pk.wpubkey_hash().expect("a compressed pubkey"); - - let redeem_script = ScriptBuf::new_v0_p2wpkh(&wpkh); - input.redeem_script = Some(redeem_script); - - let fingerprint = self.master_fingerprint; - let path = input_derivation_path()?; - let mut map = BTreeMap::new(); - map.insert(pk.inner, (fingerprint, path)); - input.bip32_derivation = map; - - let ty = PsbtSighashType::from_str("SIGHASH_ALL")?; - input.sighash_type = Some(ty); - - psbt.inputs = vec![input]; - - Ok(psbt) - } - - /// Finalizes the PSBT, in BIP174 parlance this is the 'Finalizer'. - /// This is just an example. For a production-ready PSBT Finalizer, use [rust-miniscript](https://docs.rs/miniscript/latest/miniscript/psbt/trait.PsbtExt.html#tymethod.finalize) - fn finalize_psbt(&self, mut psbt: Psbt) -> Result { - if psbt.inputs.is_empty() { - return Err(psbt::SignError::MissingInputUtxo.into()); - } - - let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect(); - let mut script_witness: Witness = Witness::new(); - script_witness.push(sigs[0].to_vec()); - script_witness.push(self.input_xpub.to_pub().to_bytes()); - - psbt.inputs[0].final_script_witness = Some(script_witness); - - // Clear all the data fields as per the spec. - psbt.inputs[0].partial_sigs = BTreeMap::new(); - psbt.inputs[0].sighash_type = None; - psbt.inputs[0].redeem_script = None; - psbt.inputs[0].witness_script = None; - psbt.inputs[0].bip32_derivation = BTreeMap::new(); - - Ok(psbt) - } - - /// Returns data for the first change address (standard BIP84 derivation path - /// "m/84h/0h/0h/1/0"). A real wallet would have access to the chain so could determine if an - /// address has been used or not. We ignore this detail and just re-use the first change address - /// without loss of generality. - fn change_address( - &self, - secp: &Secp256k1, - ) -> Result<(PublicKey, Address, DerivationPath)> { - let path = [ChildNumber::from_normal_idx(1)?, ChildNumber::from_normal_idx(0)?]; - let derived = self.account_0_xpub.derive_pub(secp, &path)?; - - let pk = derived.to_pub(); - let addr = Address::p2wpkh(&pk, NETWORK)?; - let path = path.into_derivation_path()?; - - Ok((pk, addr, path)) - } -} - -fn input_derivation_path() -> Result { - let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?; - Ok(path) -} - -fn previous_output() -> TxOut { - let script_pubkey = ScriptBuf::from_hex(INPUT_UTXO_SCRIPT_PUBKEY) - .expect("failed to parse input utxo scriptPubkey"); - let amount = Amount::from_str(INPUT_UTXO_VALUE).expect("failed to parse input utxo value"); - - TxOut { - value: amount.to_sat(), - script_pubkey, - } -} - -struct Error(Box); - -impl From for Error { - fn from(e: T) -> Self { - Error(Box::new(e)) - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} diff --git a/dash/examples/taproot-psbt.rs b/dash/examples/taproot-psbt.rs deleted file mode 100644 index f94c5ac83..000000000 --- a/dash/examples/taproot-psbt.rs +++ /dev/null @@ -1,802 +0,0 @@ -//! Example of taproot PSBT workflow - -// We use the alias `alias bt='bitcoin-cli -regtest'` for brevity. - -// Step 0 - Wipe the `regtest` data directory to start from a clean slate. - -// Step 1 - Run `bitcoind -regtest -daemon` to start the daemon. Bitcoin Core 23.0+ is required. - -// Step 2 - -// 2.1) Run `bt -named createwallet wallet_name=benefactor blank=true` to create a blank wallet with the name "benefactor" -// 2.2) Run `bt -named createwallet wallet_name=beneficiary blank=true` to create a blank wallet with the name "beneficiary" -// 2.3) Create the two aliases: -// alias bt-benefactor='bitcoin-cli -regtest -rpcwallet=benefactor' -// alias bt-beneficiary='bitcoin-cli -regtest -rpcwallet=beneficiary' -// -// 2.4) Import the example descriptors: -// bt-benefactor importdescriptors '[ -// { "desc": "tr(tprv8ZgxMBicQKsPd4arFr7sKjSnKFDVMR2JHw9Y8L9nXN4kiok4u28LpHijEudH3mMYoL4pM5UL9Bgdz2M4Cy8EzfErmU9m86ZTw6hCzvFeTg7/86\'/1\'/0\'/1/*)#jzyeered", "active": true, "timestamp": "now", "internal": true }, -// { "desc": "tr(tprv8ZgxMBicQKsPd4arFr7sKjSnKFDVMR2JHw9Y8L9nXN4kiok4u28LpHijEudH3mMYoL4pM5UL9Bgdz2M4Cy8EzfErmU9m86ZTw6hCzvFeTg7/86\'/1\'/0\'/0/*)#rkpcykf4", "active": true, "timestamp": "now" } -// ]' -// bt-beneficiary importdescriptors '[ -// { "desc": "tr(tprv8ZgxMBicQKsPe72C5c3cugP8b7AzEuNjP4NSC17Dkpqk5kaAmsL6FHwPsVxPpURVqbNwdLAbNqi8Cvdq6nycDwYdKHDjDRYcsMzfshimAUq/86\'/1\'/0\'/1/*)#w4ehwx46", "active": true, "timestamp": "now", "internal": true }, -// { "desc": "tr(tprv8ZgxMBicQKsPe72C5c3cugP8b7AzEuNjP4NSC17Dkpqk5kaAmsL6FHwPsVxPpURVqbNwdLAbNqi8Cvdq6nycDwYdKHDjDRYcsMzfshimAUq/86\'/1\'/0\'/0/*)#lpuknn9z", "active": true, "timestamp": "now" } -// ]' -// -// The xpriv and derivation path from the imported descriptors -const BENEFACTOR_XPRIV_STR: &str = "tprv8ZgxMBicQKsPd4arFr7sKjSnKFDVMR2JHw9Y8L9nXN4kiok4u28LpHijEudH3mMYoL4pM5UL9Bgdz2M4Cy8EzfErmU9m86ZTw6hCzvFeTg7"; -const BENEFICIARY_XPRIV_STR: &str = "tprv8ZgxMBicQKsPe72C5c3cugP8b7AzEuNjP4NSC17Dkpqk5kaAmsL6FHwPsVxPpURVqbNwdLAbNqi8Cvdq6nycDwYdKHDjDRYcsMzfshimAUq"; -const BIP86_DERIVATION_PATH: &str = "m/86'/1'/0'/0/0"; - -// Step 3 - -// Run `bt generatetoaddress 103 $(bt-benefactor getnewaddress '' bech32m)` to generate 103 new blocks -// with block reward being sent to a newly created P2TR address in the `benefactor` wallet. -// This will leave us with 3 mature UTXOs that can be spent. Each will be used in a different example below. - -// Step 4 - Run `bt-benefactor listunspent` to display our three spendable UTXOs. Check that everything is the same as below -// - otherwise modify it. The txids should be deterministic on regtest: - -const UTXO_SCRIPT_PUBKEY: &str = - "5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16"; -const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19"; -const UTXO_MASTER_FINGERPRINT: &str = "e61b318f"; -const ABSOLUTE_FEES_IN_SATS: u64 = 1000; - -// UTXO_1 will be used for spending example 1 -const UTXO_1: P2trUtxo = P2trUtxo { - txid: "a85d89b4666fed622281d3589474aa1f87971b54bd5d9c1899ed2e8e0447cc06", - vout: 0, - script_pubkey: UTXO_SCRIPT_PUBKEY, - pubkey: UTXO_PUBKEY, - master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: 50 * COIN_VALUE, // 50 BTC - derivation_path: BIP86_DERIVATION_PATH, -}; - -// UTXO_2 will be used for spending example 2 -const UTXO_2: P2trUtxo = P2trUtxo { - txid: "6f1c1df5862a67f4b6d1cde9a87e3c441b483ba6a140fbec2815f03aa3a5309d", - vout: 0, - script_pubkey: UTXO_SCRIPT_PUBKEY, - pubkey: UTXO_PUBKEY, - master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: 50 * COIN_VALUE, - derivation_path: BIP86_DERIVATION_PATH, -}; - -// UTXO_3 will be used for spending example 3 -const UTXO_3: P2trUtxo = P2trUtxo { - txid: "9795fed5aedca219244a396dfd7bce55c851274418383c3ab43530e3f74e5dcc", - vout: 0, - script_pubkey: UTXO_SCRIPT_PUBKEY, - pubkey: UTXO_PUBKEY, - master_fingerprint: UTXO_MASTER_FINGERPRINT, - amount_in_sats: 50 * COIN_VALUE, - derivation_path: BIP86_DERIVATION_PATH, -}; - -use std::collections::BTreeMap; -use std::str::FromStr; - -use dashcore::consensus::encode; -use dashcore::constants::COIN_VALUE; -use dashcore::hashes::Hash; -use dashcore::key::{TapTweak, XOnlyPublicKey}; -use dashcore::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP}; -use dashcore::secp256k1::Secp256k1; -use dashcore::sighash::{self, SighashCache, TapSighash, TapSighashType}; -use dashcore::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo}; -use dashcore::{ - Address, Amount, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Witness, absolute, - script, -}; -use key_wallet::bip32::{ - ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, -}; -use key_wallet::psbt::{self, Input, Output, Psbt, PsbtSighashType}; - -fn main() -> Result<(), Box> { - let secp = Secp256k1::new(); - - println!("\n----------------"); - println!("\nSTART EXAMPLE 1 - P2TR with a BIP86 commitment, signed with internal key\n"); - - // Just some addresses for outputs from our wallets. Not really important. - let to_address = - Address::from_str("bcrt1p0p3rvwww0v9znrclp00uneq8ytre9kj922v8fxhnezm3mgsmn9usdxaefc")? - .require_network(Network::Regtest)?; - let change_address = - Address::from_str("bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76")? - .require_network(Network::Regtest)?; - let amount_to_send_in_sats = COIN_VALUE; - let change_amount = UTXO_1 - .amount_in_sats - .checked_sub(amount_to_send_in_sats) - .and_then(|x| x.checked_sub(ABSOLUTE_FEES_IN_SATS)) - .ok_or("Fees more than input amount!")?; - - let tx_hex_string = encode::serialize_hex(&generate_bip86_key_spend_tx( - &secp, - // The master extended private key from the descriptor in step 4 - ExtendedPrivKey::from_str(BENEFACTOR_XPRIV_STR)?, - // Set these fields with valid data for the UTXO from step 5 above - UTXO_1, - vec![ - TxOut { - value: amount_to_send_in_sats, - script_pubkey: to_address.script_pubkey(), - }, - TxOut { - value: change_amount, - script_pubkey: change_address.script_pubkey(), - }, - ], - )?); - println!( - "\nYou should now be able to broadcast the following transaction: \n\n{}", - tx_hex_string - ); - - println!("\nEND EXAMPLE 1\n"); - println!("----------------\n"); - - println!("START EXAMPLE 2 - Script path spending of inheritance UTXO\n"); - - { - let beneficiary = - BeneficiaryWallet::new(ExtendedPrivKey::from_str(BENEFICIARY_XPRIV_STR)?)?; - - let mut benefactor = BenefactorWallet::new( - ExtendedPrivKey::from_str(BENEFACTOR_XPRIV_STR)?, - beneficiary.master_xpub(), - )?; - let (tx, psbt) = benefactor.create_inheritance_funding_tx( - absolute::LockTime::from_height(1000).unwrap(), - UTXO_2, - )?; - let tx_hex = encode::serialize_hex(&tx); - - println!("Inheritance funding tx hex:\n\n{}", tx_hex); - // You can now broadcast the transaction hex: - // bt sendrawtransaction ... - // - // And mine a block to confirm the transaction: - // bt generatetoaddress 1 $(bt-benefactor getnewaddress '' 'bech32m') - - let spending_tx = beneficiary.spend_inheritance( - psbt, - absolute::LockTime::from_height(1000).unwrap(), - to_address, - )?; - let spending_tx_hex = encode::serialize_hex(&spending_tx); - println!("\nInheritance spending tx hex:\n\n{}", spending_tx_hex); - // If you try to broadcast now, the transaction will be rejected as it is timelocked. - // First mine 900 blocks so we're sure we are over the 1000 block locktime: - // bt generatetoaddress 900 $(bt-benefactor getnewaddress '' 'bech32m') - // Then broadcast the transaction with `bt sendrawtransaction ...` - } - - println!("\nEND EXAMPLE 2\n"); - println!("----------------\n"); - - println!("START EXAMPLE 3 - Key path spending of inheritance UTXO\n"); - - { - let beneficiary = - BeneficiaryWallet::new(ExtendedPrivKey::from_str(BENEFICIARY_XPRIV_STR)?)?; - - let mut benefactor = BenefactorWallet::new( - ExtendedPrivKey::from_str(BENEFACTOR_XPRIV_STR)?, - beneficiary.master_xpub(), - )?; - let (tx, _) = benefactor.create_inheritance_funding_tx( - absolute::LockTime::from_height(2000).unwrap(), - UTXO_3, - )?; - let tx_hex = encode::serialize_hex(&tx); - - println!("Inheritance funding tx hex:\n\n{}", tx_hex); - // You can now broadcast the transaction hex: - // bt sendrawtransaction ... - // - // And mine a block to confirm the transaction: - // bt generatetoaddress 1 $(bt-benefactor getnewaddress '' 'bech32m') - - // At some point we may want to extend the locktime further into the future for the beneficiary. - // We can do this by "refreshing" the inheritance transaction as the benefactor. This effectively - // spends the inheritance transaction via the key path of the taproot output, and is not encumbered - // by the timelock so we can spend it immediately. We set up a new output similar to the first with - // a locktime that is 'locktime_delta' blocks greater. - let (tx, _) = benefactor.refresh_tx(1000)?; - let tx_hex = encode::serialize_hex(&tx); - - println!("\nRefreshed inheritance tx hex:\n\n{}\n", tx_hex); - - println!("\nEND EXAMPLE 3\n"); - println!("----------------\n"); - } - - Ok(()) -} - -struct P2trUtxo<'a> { - txid: &'a str, - vout: u32, - script_pubkey: &'a str, - pubkey: &'a str, - master_fingerprint: &'a str, - amount_in_sats: u64, - derivation_path: &'a str, -} - -fn generate_bip86_key_spend_tx( - secp: &secp256k1::Secp256k1, - master_xpriv: ExtendedPrivKey, - input_utxo: P2trUtxo, - outputs: Vec, -) -> Result> { - let from_amount = input_utxo.amount_in_sats; - let input_pubkey = XOnlyPublicKey::from_str(input_utxo.pubkey)?; - - // CREATOR + UPDATER - let tx1 = Transaction { - version: 2, - lock_time: 0, - input: vec![TxIn { - previous_output: OutPoint { - txid: input_utxo.txid.parse()?, - vout: input_utxo.vout, - }, - script_sig: ScriptBuf::new(), - sequence: 0xFFFFFFFF, // Ignore nSequence. - witness: Witness::default(), - }], - output: outputs, - special_transaction_payload: None, - }; - let mut psbt = Psbt::from_unsigned_tx(tx1)?; - - let mut origins = BTreeMap::new(); - origins.insert( - input_pubkey, - ( - vec![], - ( - Fingerprint::from_str(input_utxo.master_fingerprint)?, - DerivationPath::from_str(input_utxo.derivation_path)?, - ), - ), - ); - - let mut input = Input { - witness_utxo: { - let script_pubkey = ScriptBuf::from_hex(input_utxo.script_pubkey) - .expect("failed to parse input utxo scriptPubkey"); - let amount = Amount::from_sat(from_amount); - - Some(TxOut { - value: amount.to_sat(), - script_pubkey, - }) - }, - tap_key_origins: origins, - ..Default::default() - }; - let ty = PsbtSighashType::from_str("SIGHASH_ALL")?; - input.sighash_type = Some(ty); - input.tap_internal_key = Some(input_pubkey); - psbt.inputs = vec![input]; - - // SIGNER - let unsigned_tx = psbt.unsigned_tx.clone(); - psbt.inputs.iter_mut().enumerate().try_for_each::<_, Result<(), Box>>( - |(vout, input)| { - let hash_ty = input - .sighash_type - .and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok()) - .unwrap_or(TapSighashType::All); - let hash = SighashCache::new(&unsigned_tx).taproot_key_spend_signature_hash( - vout, - &sighash::Prevouts::All(&[TxOut { - value: from_amount, - script_pubkey: ScriptBuf::from_hex(input_utxo.script_pubkey)?, - }]), - hash_ty, - )?; - - let (_, (_, derivation_path)) = input - .tap_key_origins - .get(&input.tap_internal_key.ok_or("Internal key missing in PSBT")?) - .ok_or("Missing taproot key origin")?; - - let secret_key = master_xpriv.derive_priv(secp, &derivation_path)?.to_priv().inner; - sign_psbt_taproot( - &secret_key, - input.tap_internal_key.unwrap(), - None, - input, - hash, - hash_ty, - secp, - ); - - Ok(()) - }, - )?; - - // FINALIZER - psbt.inputs.iter_mut().for_each(|input| { - let mut script_witness: Witness = Witness::new(); - script_witness.push(input.tap_key_sig.unwrap().to_vec()); - input.final_script_witness = Some(script_witness); - - // Clear all the data fields as per the spec. - input.partial_sigs = BTreeMap::new(); - input.sighash_type = None; - input.redeem_script = None; - input.witness_script = None; - input.bip32_derivation = BTreeMap::new(); - }); - - // EXTRACTOR - let tx = psbt.extract_tx(); - tx.verify(|_| { - Some(TxOut { - value: from_amount, - script_pubkey: ScriptBuf::from_hex(input_utxo.script_pubkey).unwrap(), - }) - }) - .expect("failed to verify transaction"); - - Ok(tx) -} - -/// A wallet that allows creating and spending from an inheritance directly via the key path for purposes -/// of refreshing the inheritance timelock or changing other spending conditions. -struct BenefactorWallet { - master_xpriv: ExtendedPrivKey, - beneficiary_xpub: ExtendedPubKey, - current_spend_info: Option, - next_psbt: Option, - secp: Secp256k1, - next: ChildNumber, -} - -impl BenefactorWallet { - fn new( - master_xpriv: ExtendedPrivKey, - beneficiary_xpub: ExtendedPubKey, - ) -> Result> { - Ok(Self { - master_xpriv, - beneficiary_xpub, - current_spend_info: None, - next_psbt: None, - secp: Secp256k1::new(), - next: ChildNumber::from_normal_idx(0).expect("Zero is a valid child number"), - }) - } - - fn time_lock_script( - locktime: absolute::LockTime, - beneficiary_key: XOnlyPublicKey, - ) -> ScriptBuf { - script::Builder::new() - .push_int(locktime.to_consensus_u32() as i64) - .push_opcode(OP_CLTV) - .push_opcode(OP_DROP) - .push_x_only_key(&beneficiary_key) - .push_opcode(OP_CHECKSIG) - .into_script() - } - - fn create_inheritance_funding_tx( - &mut self, - lock_time: absolute::LockTime, - input_utxo: P2trUtxo, - ) -> Result<(Transaction, Psbt), Box> { - if let ChildNumber::Normal { - index, - } = self.next - && index > 0 - && self.current_spend_info.is_some() - { - return Err("Transaction already exists, use refresh_inheritance_timelock to refresh the timelock".into()); - } - // We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure - // that we use an unhardened path so we can make use of xpubs. - let derivation_path = DerivationPath::from_str(&format!("m/101/1/0/0/{}", self.next))?; - let internal_keypair = - self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_keypair(&self.secp); - let beneficiary_key = - self.beneficiary_xpub.derive_pub(&self.secp, &derivation_path)?.to_x_only_pub(); - - // Build up the leaf script and combine with internal key into a taproot commitment - let script = Self::time_lock_script(lock_time, beneficiary_key); - let leaf_hash = script.tapscript_leaf_hash(); - - let taproot_spend_info = TaprootBuilder::new() - .add_leaf(0, script.clone())? - .finalize(&self.secp, internal_keypair.x_only_public_key().0) - .expect("Should be finalizable"); - self.current_spend_info = Some(taproot_spend_info.clone()); - let script_pubkey = ScriptBuf::new_v1_p2tr( - &self.secp, - taproot_spend_info.internal_key(), - taproot_spend_info.merkle_root(), - ); - let value = input_utxo.amount_in_sats - ABSOLUTE_FEES_IN_SATS; - - // Spend a normal BIP86-like output as an input in our inheritance funding transaction - let tx = generate_bip86_key_spend_tx( - &self.secp, - self.master_xpriv, - input_utxo, - vec![TxOut { - script_pubkey: script_pubkey.clone(), - value, - }], - )?; - - // CREATOR + UPDATER - let next_tx = Transaction { - version: 2, - lock_time: lock_time.to_consensus_u32(), - input: vec![TxIn { - previous_output: OutPoint { - txid: tx.txid(), - vout: 0, - }, - script_sig: ScriptBuf::new(), - sequence: 0xFFFFFFFD, // enable locktime and opt-in RBF - witness: Witness::default(), - }], - output: vec![], - special_transaction_payload: None, - }; - let mut next_psbt = Psbt::from_unsigned_tx(next_tx)?; - let mut origins = BTreeMap::new(); - origins.insert( - beneficiary_key, - (vec![leaf_hash], (self.beneficiary_xpub.fingerprint(), derivation_path.clone())), - ); - origins.insert( - internal_keypair.x_only_public_key().0, - (vec![], (self.master_xpriv.fingerprint(&self.secp), derivation_path)), - ); - let ty = PsbtSighashType::from_str("SIGHASH_ALL")?; - let mut tap_scripts = BTreeMap::new(); - tap_scripts.insert( - taproot_spend_info.control_block(&(script.clone(), LeafVersion::TapScript)).unwrap(), - (script, LeafVersion::TapScript), - ); - - let input = Input { - witness_utxo: { - let amount = Amount::from_sat(value); - - Some(TxOut { - value: amount.to_sat(), - script_pubkey, - }) - }, - tap_key_origins: origins, - tap_merkle_root: taproot_spend_info.merkle_root(), - sighash_type: Some(ty), - tap_internal_key: Some(internal_keypair.x_only_public_key().0), - tap_scripts, - ..Default::default() - }; - - next_psbt.inputs = vec![input]; - self.next_psbt = Some(next_psbt.clone()); - - self.next.increment()?; - Ok((tx, next_psbt)) - } - - fn refresh_tx( - &mut self, - lock_time_delta: u32, - ) -> Result<(Transaction, Psbt), Box> { - if let Some(ref spend_info) = self.current_spend_info.clone() { - let mut psbt = self.next_psbt.clone().expect("Should have next_psbt"); - let input = &mut psbt.inputs[0]; - let input_value = input.witness_utxo.as_ref().unwrap().value; - let output_value = input_value - ABSOLUTE_FEES_IN_SATS; - - // We use some other derivation path in this example for our inheritance protocol. The important thing is to ensure - // that we use an unhardened path so we can make use of xpubs. - let new_derivation_path = - DerivationPath::from_str(&format!("m/101/1/0/0/{}", self.next))?; - let new_internal_keypair = self - .master_xpriv - .derive_priv(&self.secp, &new_derivation_path)? - .to_keypair(&self.secp); - let beneficiary_key = - self.beneficiary_xpub.derive_pub(&self.secp, &new_derivation_path)?.to_x_only_pub(); - - // Build up the leaf script and combine with internal key into a taproot commitment - let lock_time = - absolute::LockTime::from_height(psbt.unsigned_tx.lock_time + lock_time_delta) - .unwrap(); - let script = Self::time_lock_script(lock_time, beneficiary_key); - let leaf_hash = script.tapscript_leaf_hash(); - - let taproot_spend_info = TaprootBuilder::new() - .add_leaf(0, script.clone())? - .finalize(&self.secp, new_internal_keypair.x_only_public_key().0) - .expect("Should be finalizable"); - self.current_spend_info = Some(taproot_spend_info.clone()); - let prevout_script_pubkey = input.witness_utxo.as_ref().unwrap().script_pubkey.clone(); - let output_script_pubkey = ScriptBuf::new_v1_p2tr( - &self.secp, - taproot_spend_info.internal_key(), - taproot_spend_info.merkle_root(), - ); - - psbt.unsigned_tx.output = vec![TxOut { - script_pubkey: output_script_pubkey.clone(), - value: output_value, - }]; - psbt.outputs = vec![Output::default()]; - psbt.unsigned_tx.lock_time = 0; - - let hash_ty = input - .sighash_type - .and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok()) - .unwrap_or(TapSighashType::All); - let hash = SighashCache::new(&psbt.unsigned_tx).taproot_key_spend_signature_hash( - 0, - &sighash::Prevouts::All(&[TxOut { - value: input_value, - script_pubkey: prevout_script_pubkey, - }]), - hash_ty, - )?; - - { - let (_, (_, derivation_path)) = input - .tap_key_origins - .get(&input.tap_internal_key.ok_or("Internal key missing in PSBT")?) - .ok_or("Missing taproot key origin")?; - let secret_key = - self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner; - sign_psbt_taproot( - &secret_key, - spend_info.internal_key(), - None, - input, - hash, - hash_ty, - &self.secp, - ); - } - - // FINALIZER - psbt.inputs.iter_mut().for_each(|input| { - let mut script_witness: Witness = Witness::new(); - script_witness.push(input.tap_key_sig.unwrap().to_vec()); - input.final_script_witness = Some(script_witness); - - // Clear all the data fields as per the spec. - input.partial_sigs = BTreeMap::new(); - input.sighash_type = None; - input.redeem_script = None; - input.witness_script = None; - input.bip32_derivation = BTreeMap::new(); - }); - - // EXTRACTOR - let tx = psbt.extract_tx(); - tx.verify(|_| { - Some(TxOut { - value: input_value, - script_pubkey: output_script_pubkey.clone(), - }) - }) - .expect("failed to verify transaction"); - - let next_tx = Transaction { - version: 2, - lock_time: lock_time.to_consensus_u32(), - input: vec![TxIn { - previous_output: OutPoint { - txid: tx.txid(), - vout: 0, - }, - script_sig: ScriptBuf::new(), - sequence: 0xFFFFFFFD, // enable locktime and opt-in RBF - witness: Witness::default(), - }], - output: vec![], - special_transaction_payload: None, - }; - let mut next_psbt = Psbt::from_unsigned_tx(next_tx)?; - let mut origins = BTreeMap::new(); - origins.insert( - beneficiary_key, - (vec![leaf_hash], (self.beneficiary_xpub.fingerprint(), new_derivation_path)), - ); - let ty = PsbtSighashType::from_str("SIGHASH_ALL")?; - let mut tap_scripts = BTreeMap::new(); - tap_scripts.insert( - taproot_spend_info - .control_block(&(script.clone(), LeafVersion::TapScript)) - .unwrap(), - (script, LeafVersion::TapScript), - ); - - let input = Input { - witness_utxo: { - let script_pubkey = output_script_pubkey; - let amount = Amount::from_sat(output_value); - - Some(TxOut { - value: amount.to_sat(), - script_pubkey, - }) - }, - tap_key_origins: origins, - tap_merkle_root: taproot_spend_info.merkle_root(), - sighash_type: Some(ty), - tap_internal_key: Some(new_internal_keypair.x_only_public_key().0), - tap_scripts, - ..Default::default() - }; - - next_psbt.inputs = vec![input]; - self.next_psbt = Some(next_psbt.clone()); - - self.next.increment()?; - Ok((tx, next_psbt)) - } else { - Err("No current_spend_info available. Create an inheritance tx first.".into()) - } - } -} - -/// A wallet that allows spending from an inheritance locked to a P2TR UTXO via a script path -/// after some expiry using CLTV. -struct BeneficiaryWallet { - master_xpriv: ExtendedPrivKey, - secp: secp256k1::Secp256k1, -} - -impl BeneficiaryWallet { - fn new(master_xpriv: ExtendedPrivKey) -> Result> { - Ok(Self { - master_xpriv, - secp: Secp256k1::new(), - }) - } - - fn master_xpub(&self) -> ExtendedPubKey { - ExtendedPubKey::from_priv(&self.secp, &self.master_xpriv) - } - - fn spend_inheritance( - &self, - mut psbt: Psbt, - lock_time: absolute::LockTime, - to_address: Address, - ) -> Result> { - let input_value = psbt.inputs[0].witness_utxo.as_ref().unwrap().value; - let input_script_pubkey = - psbt.inputs[0].witness_utxo.as_ref().unwrap().script_pubkey.clone(); - psbt.unsigned_tx.lock_time = lock_time.to_consensus_u32(); - psbt.unsigned_tx.output = vec![TxOut { - script_pubkey: to_address.script_pubkey(), - value: input_value - ABSOLUTE_FEES_IN_SATS, - }]; - psbt.outputs = vec![Output::default()]; - let unsigned_tx = psbt.unsigned_tx.clone(); - - // SIGNER - for (x_only_pubkey, (leaf_hashes, (_, derivation_path))) in - &psbt.inputs[0].tap_key_origins.clone() - { - let secret_key = - self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner; - for lh in leaf_hashes { - let hash_ty = TapSighashType::All; - let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash( - 0, - &sighash::Prevouts::All(&[TxOut { - value: input_value, - script_pubkey: input_script_pubkey.clone(), - }]), - *lh, - hash_ty, - )?; - sign_psbt_taproot( - &secret_key, - *x_only_pubkey, - Some(*lh), - &mut psbt.inputs[0], - hash, - hash_ty, - &self.secp, - ); - } - } - - // FINALIZER - psbt.inputs.iter_mut().for_each(|input| { - let mut script_witness: Witness = Witness::new(); - for (_, signature) in input.tap_script_sigs.iter() { - script_witness.push(signature.to_vec()); - } - for (control_block, (script, _)) in input.tap_scripts.iter() { - script_witness.push(script.to_bytes()); - script_witness.push(control_block.serialize()); - } - input.final_script_witness = Some(script_witness); - - // Clear all the data fields as per the spec. - input.partial_sigs = BTreeMap::new(); - input.sighash_type = None; - input.redeem_script = None; - input.witness_script = None; - input.bip32_derivation = BTreeMap::new(); - input.tap_script_sigs = BTreeMap::new(); - input.tap_scripts = BTreeMap::new(); - input.tap_key_sig = None; - }); - - // EXTRACTOR - let tx = psbt.extract_tx(); - tx.verify(|_| { - Some(TxOut { - value: input_value, - script_pubkey: input_script_pubkey.clone(), - }) - }) - .expect("failed to verify transaction"); - - Ok(tx) - } -} - -// Lifted and modified from BDK at https://github.com/bitcoindevkit/bdk/blob/8fbe40a9181cc9e22cabfc04d57dac5d459da87d/src/wallet/signer.rs#L469-L503 - -// Bitcoin Dev Kit -// Written in 2020 by Alekos Filini -// -// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -// Calling this with `leaf_hash` = `None` will sign for key-spend -fn sign_psbt_taproot( - secret_key: &secp256k1::SecretKey, - pubkey: XOnlyPublicKey, - leaf_hash: Option, - psbt_input: &mut psbt::Input, - hash: TapSighash, - hash_ty: TapSighashType, - secp: &Secp256k1, -) { - let keypair = secp256k1::Keypair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); - let keypair = match leaf_hash { - None => keypair.tap_tweak(secp, psbt_input.tap_merkle_root).to_inner(), - Some(_) => keypair, // no tweak for script spend - }; - - let sig = secp.sign_schnorr(hash.as_byte_array(), &keypair); - - let final_signature = taproot::Signature { - sig, - hash_ty, - }; - - if let Some(lh) = leaf_hash { - psbt_input.tap_script_sigs.insert((pubkey, lh), final_signature); - } else { - psbt_input.tap_key_sig = Some(final_signature); - } -} diff --git a/dash/src/address.rs b/dash/src/address.rs index 25835d55d..717e4013e 100644 --- a/dash/src/address.rs +++ b/dash/src/address.rs @@ -41,7 +41,7 @@ //! dashcore = { version = "...", features = ["rand-std"] } //! ``` -use core::convert::{TryFrom, TryInto}; +use core::convert::TryInto; use core::fmt; use core::marker::PhantomData; use core::str::FromStr; @@ -51,21 +51,14 @@ use crate::blockdata::constants::{ MAX_SCRIPT_ELEMENT_SIZE, PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_TEST, }; -use crate::blockdata::opcodes; -use crate::blockdata::opcodes::all::*; -use crate::blockdata::script::{ - self, Instruction, PushBytes, PushBytesBuf, PushBytesErrorReport, Script, ScriptBuf, -}; -use crate::crypto::key::{PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey}; -use crate::error::ParseIntError; +use crate::blockdata::script::{Script, ScriptBuf}; +use crate::crypto::key::PublicKey; use crate::hash_types::{PubkeyHash, ScriptHash}; use crate::prelude::*; -use crate::taproot::TapNodeHash; use bech32; use dash_network::Network; -use hashes::{Hash, HashEngine, sha256}; +use hashes::Hash; use internals::write_err; -use secp256k1::{Secp256k1, Verification, XOnlyPublicKey}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -79,28 +72,18 @@ pub enum Error { Bech32(bech32::Error), /// The bech32 payload was empty. EmptyBech32Payload, - /// The wrong checksum algorithm was used. See BIP-0350. + /// The wrong checksum algorithm was used. InvalidBech32Variant { - /// Bech32 variant that is required by the used Witness version. + /// Expected Bech32 variant. expected: bech32::Variant, /// The actual Bech32 variant encoded in the address representation. found: bech32::Variant, }, - /// Script version must be 0 to 16 inclusive. - InvalidWitnessVersion(u8), - /// Unable to parse witness version from string. - UnparsableWitnessVersion(ParseIntError), - /// Dash script opcode does not match any known witness version, the script is malformed. - MalformedWitnessVersion, - /// The witness program must be between 2 and 40 bytes in length. - InvalidWitnessProgramLength(usize), - /// A v0 witness program must be either of length 20 or 32. - InvalidSegwitV0ProgramLength(usize), /// An uncompressed pubkey was used where it is not allowed. UncompressedPubkey, /// Address size more than 520 bytes is not allowed. ExcessiveScriptSize, - /// Script is not a p2pkh, p2sh or witness program. + /// Script is not a p2pkh or p2sh. UnrecognizedScript, /// Address type is either invalid or not supported in rust-dashcore. UnknownAddressType(String), @@ -121,20 +104,36 @@ impl fmt::Display for Error { Error::Base58(ref e) => write_err!(f, "base58 address encoding error"; e), Error::Bech32(ref e) => write_err!(f, "bech32 address encoding error"; e), Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"), - Error::InvalidBech32Variant { expected, found } => write!(f, "invalid bech32 checksum variant found {:?} when {:?} was expected", found, expected), - Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v), - Error::UnparsableWitnessVersion(ref e) => write_err!(f, "incorrect format of a witness version byte"; e), - Error::MalformedWitnessVersion => f.write_str("dash script opcode does not match any known witness version, the script is malformed"), - Error::InvalidWitnessProgramLength(l) => write!(f, "the witness program must be between 2 and 40 bytes in length: length={}", l), - Error::InvalidSegwitV0ProgramLength(l) => write!(f, "a v0 witness program must be either of length 20 or 32 bytes: length={}", l), - Error::UncompressedPubkey => write!(f, "an uncompressed pubkey was used where it is not allowed"), + Error::InvalidBech32Variant { + expected, + found, + } => write!( + f, + "invalid bech32 checksum variant found {:?} when {:?} was expected", + found, expected + ), + Error::UncompressedPubkey => { + write!(f, "an uncompressed pubkey was used where it is not allowed") + } Error::ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"), - Error::UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"), - Error::UnknownAddressType(ref s) => write!(f, "unknown address type: '{}' is either invalid or not supported in rust-dash", s), - Error::NetworkValidation { required, found, ref address } => { + Error::UnrecognizedScript => write!(f, "script is not a p2pkh or p2sh"), + Error::UnknownAddressType(ref s) => write!( + f, + "unknown address type: '{}' is either invalid or not supported in rust-dash", + s + ), + Error::NetworkValidation { + required, + found, + ref address, + } => { write!(f, "address ")?; address.fmt_internal(f)?; // Using fmt_internal in order to remove the "Address(..)" wrapper - write!(f, " belongs to network {} which is different from required {}", found, required) + write!( + f, + " belongs to network {} which is different from required {}", + found, required + ) } } } @@ -148,15 +147,10 @@ impl std::error::Error for Error { match self { Base58(e) => Some(e), Bech32(e) => Some(e), - UnparsableWitnessVersion(e) => Some(e), EmptyBech32Payload | InvalidBech32Variant { .. } - | InvalidWitnessVersion(_) - | MalformedWitnessVersion - | InvalidWitnessProgramLength(_) - | InvalidSegwitV0ProgramLength(_) | UncompressedPubkey | ExcessiveScriptSize | UnrecognizedScript @@ -191,12 +185,6 @@ pub enum AddressType { P2pkh, /// Pay to script hash. P2sh, - /// Pay to witness pubkey hash. - P2wpkh, - /// Pay to witness script hash. - P2wsh, - /// Pay to taproot. - P2tr, } impl fmt::Display for AddressType { @@ -204,9 +192,6 @@ impl fmt::Display for AddressType { f.write_str(match *self { AddressType::P2pkh => "p2pkh", AddressType::P2sh => "p2sh", - AddressType::P2wpkh => "p2wpkh", - AddressType::P2wsh => "p2wsh", - AddressType::P2tr => "p2tr", }) } } @@ -217,216 +202,11 @@ impl FromStr for AddressType { match s { "p2pkh" => Ok(AddressType::P2pkh), "p2sh" => Ok(AddressType::P2sh), - "p2wpkh" => Ok(AddressType::P2wpkh), - "p2wsh" => Ok(AddressType::P2wsh), - "p2tr" => Ok(AddressType::P2tr), _ => Err(Error::UnknownAddressType(s.to_owned())), } } } -/// Version of the witness program. -/// -/// Helps limit possible versions of the witness according to the specification. If a plain `u8` -/// type was used instead it would mean that the version may be > 16, which would be incorrect. -/// -/// First byte of `scriptPubkey` in transaction output for transactions starting with opcodes -/// ranging from 0 to 16 (inclusive). -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[repr(u8)] -pub enum WitnessVersion { - /// Initial version of witness program. Used for P2WPKH and P2WPK outputs - V0 = 0, - /// Version of witness program used for Taproot P2TR outputs. - V1 = 1, - /// Future (unsupported) version of witness program. - V2 = 2, - /// Future (unsupported) version of witness program. - V3 = 3, - /// Future (unsupported) version of witness program. - V4 = 4, - /// Future (unsupported) version of witness program. - V5 = 5, - /// Future (unsupported) version of witness program. - V6 = 6, - /// Future (unsupported) version of witness program. - V7 = 7, - /// Future (unsupported) version of witness program. - V8 = 8, - /// Future (unsupported) version of witness program. - V9 = 9, - /// Future (unsupported) version of witness program. - V10 = 10, - /// Future (unsupported) version of witness program. - V11 = 11, - /// Future (unsupported) version of witness program. - V12 = 12, - /// Future (unsupported) version of witness program. - V13 = 13, - /// Future (unsupported) version of witness program. - V14 = 14, - /// Future (unsupported) version of witness program. - V15 = 15, - /// Future (unsupported) version of witness program. - V16 = 16, -} - -// /// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without -// /// any prefix or suffix. -// impl fmt::Display for WitnessVersion { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u8) } -// } - -impl FromStr for WitnessVersion { - type Err = Error; - - fn from_str(s: &str) -> Result { - let version: u8 = crate::parse::int(s).map_err(Error::UnparsableWitnessVersion)?; - WitnessVersion::try_from(version) - } -} - -impl WitnessVersion { - /// Returns integer version number representation for a given [`WitnessVersion`] value. - /// - /// NB: this is not the same as an integer representation of the opcode signifying witness - /// version in dashcore script. Thus, there is no function to directly convert witness version - /// into a byte since the conversion requires context (dashcore script or just a version number). - pub fn to_num(self) -> u8 { - self as u8 - } - - /// Determines the checksum variant. See BIP-0350 for specification. - pub fn bech32_variant(&self) -> bech32::Variant { - match self { - WitnessVersion::V0 => bech32::Variant::Bech32, - _ => bech32::Variant::Bech32m, - } - } -} - -impl TryFrom for WitnessVersion { - type Error = Error; - - /// Converts 5-bit unsigned integer value matching single symbol from Bech32(m) address encoding - /// ([`bech32::u5`]) into [`WitnessVersion`] variant. - /// - /// # Returns - /// Version of the Witness program. - /// - /// # Errors - /// If the integer does not correspond to any witness version, errors with - /// [`Error::InvalidWitnessVersion`]. - fn try_from(value: bech32::u5) -> Result { - Self::try_from(value.to_u8()) - } -} - -impl TryFrom for WitnessVersion { - type Error = Error; - - /// Converts an 8-bit unsigned integer value into [`WitnessVersion`] variant. - /// - /// # Returns - /// Version of the Witness program. - /// - /// # Errors - /// If the integer does not correspond to any witness version, errors with - /// [`Error::InvalidWitnessVersion`]. - fn try_from(no: u8) -> Result { - use WitnessVersion::*; - - Ok(match no { - 0 => V0, - 1 => V1, - 2 => V2, - 3 => V3, - 4 => V4, - 5 => V5, - 6 => V6, - 7 => V7, - 8 => V8, - 9 => V9, - 10 => V10, - 11 => V11, - 12 => V12, - 13 => V13, - 14 => V14, - 15 => V15, - 16 => V16, - wrong => return Err(Error::InvalidWitnessVersion(wrong)), - }) - } -} - -impl TryFrom for WitnessVersion { - type Error = Error; - - /// Converts dashcore script opcode into [`WitnessVersion`] variant. - /// - /// # Returns - /// Version of the Witness program (for opcodes in range of `OP_0`..`OP_16`). - /// - /// # Errors - /// If the opcode does not correspond to any witness version, errors with - /// [`Error::MalformedWitnessVersion`]. - fn try_from(opcode: opcodes::All) -> Result { - match opcode.to_u8() { - 0 => Ok(WitnessVersion::V0), - version if version >= OP_PUSHNUM_1.to_u8() && version <= OP_PUSHNUM_16.to_u8() => { - WitnessVersion::try_from(version - OP_PUSHNUM_1.to_u8() + 1) - } - _ => Err(Error::MalformedWitnessVersion), - } - } -} - -impl<'a> TryFrom> for WitnessVersion { - type Error = Error; - - /// Converts dashcore script [`Instruction`] (parsed opcode) into [`WitnessVersion`] variant. - /// - /// # Returns - /// Version of the Witness program for [`Instruction::Op`] and [`Instruction::PushBytes`] with - /// byte value within `1..=16` range. - /// - /// # Errors - /// If the opcode does not correspond to any witness version, errors with - /// [`Error::MalformedWitnessVersion`] for the rest of opcodes. - fn try_from(instruction: Instruction) -> Result { - match instruction { - Instruction::Op(op) => WitnessVersion::try_from(op), - Instruction::PushBytes(bytes) if bytes.is_empty() => Ok(WitnessVersion::V0), - Instruction::PushBytes(_) => Err(Error::MalformedWitnessVersion), - } - } -} - -impl From for bech32::u5 { - /// Converts [`WitnessVersion`] instance into corresponding Bech32(m) u5-value ([`bech32::u5`]). - fn from(version: WitnessVersion) -> Self { - bech32::u5::try_from_u8(version.to_num()).expect("WitnessVersion must be 0..=16") - } -} - -impl From for opcodes::All { - /// Converts [`WitnessVersion`] instance into corresponding Dash scriptopcode (`OP_0`..`OP_16`). - fn from(version: WitnessVersion) -> opcodes::All { - match version { - WitnessVersion::V0 => OP_PUSHBYTES_0, - no => opcodes::All::from(OP_PUSHNUM_1.to_u8() + no.to_num() - 1), - } - } -} - -/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without -/// any prefix or suffix. -impl fmt::Display for WitnessVersion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", *self as u8) - } -} - /// The method used to produce an address. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] @@ -435,8 +215,6 @@ pub enum Payload { PubkeyHash(PubkeyHash), /// P2SH address. ScriptHash(ScriptHash), - /// Segwit address. - WitnessProgram(WitnessProgram), } impl Payload { @@ -447,51 +225,6 @@ impl Payload { } } } - -/// Witness program as defined in BIP141. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WitnessProgram { - /// The witness program version. - version: WitnessVersion, - /// The witness program. (Between 2 and 40 bytes) - program: PushBytesBuf, -} - -impl WitnessProgram { - /// Creates a new witness program. - pub fn new

(version: WitnessVersion, program: P) -> Result - where - P: TryInto, -

>::Error: PushBytesErrorReport, - { - let program = program - .try_into() - .map_err(|error| Error::InvalidWitnessProgramLength(error.input_len()))?; - if program.len() < 2 || program.len() > 40 { - return Err(Error::InvalidWitnessProgramLength(program.len())); - } - - // Specific segwit v0 check. These addresses can never spend funds sent to them. - if version == WitnessVersion::V0 && (program.len() != 20 && program.len() != 32) { - return Err(Error::InvalidSegwitV0ProgramLength(program.len())); - } - Ok(WitnessProgram { - version, - program, - }) - } - - /// Returns the witness program version. - pub fn version(&self) -> WitnessVersion { - self.version - } - - /// Returns the witness program. - pub fn program(&self) -> &PushBytes { - &self.program - } -} - impl Payload { /// Constructs a [Payload] from an output script (`scriptPubkey`). pub fn from_script(script: &Script) -> Result { @@ -501,14 +234,6 @@ impl Payload { } else if script.is_p2sh() { let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long"); Payload::ScriptHash(ScriptHash::from_byte_array(bytes)) - } else if script.is_witness_program() { - let opcode = script.first_opcode().expect("witness_version guarantees len() > 4"); - - let witness_program = script.as_bytes()[2..].to_vec(); - - let witness_program = - WitnessProgram::new(WitnessVersion::try_from(opcode)?, witness_program)?; - Payload::WitnessProgram(witness_program) } else { return Err(Error::UnrecognizedScript); }) @@ -519,7 +244,6 @@ impl Payload { match *self { Payload::PubkeyHash(ref hash) => ScriptBuf::new_p2pkh(hash), Payload::ScriptHash(ref hash) => ScriptBuf::new_p2sh(hash), - Payload::WitnessProgram(ref prog) => ScriptBuf::new_witness_program(prog), } } @@ -533,10 +257,7 @@ impl Payload { Payload::ScriptHash(ref hash) if script.is_p2sh() => { &script.as_bytes()[2..22] == >::as_ref(hash) } - Payload::WitnessProgram(ref prog) if script.is_witness_program() => { - &script.as_bytes()[2..] == prog.program.as_bytes() - } - Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false, + Payload::PubkeyHash(_) | Payload::ScriptHash(_) => false, } } @@ -555,66 +276,12 @@ impl Payload { Ok(Payload::ScriptHash(script.script_hash())) } - /// Create a witness pay to public key payload from a public key - pub fn p2wpkh(pk: &PublicKey) -> Result { - let prog = WitnessProgram::new( - WitnessVersion::V0, - pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?, - )?; - Ok(Payload::WitnessProgram(prog)) - } - - /// Create a pay to script payload that embeds a witness pay to public key - pub fn p2shwpkh(pk: &PublicKey) -> Result { - let builder = script::Builder::new() - .push_int(0) - .push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?); - - Ok(Payload::ScriptHash(builder.into_script().script_hash())) - } - - /// Create a witness pay to script hash payload. - pub fn p2wsh(script: &Script) -> Payload { - let prog = WitnessProgram::new(WitnessVersion::V0, script.wscript_hash()) - .expect("wscript_hash has len 32 compatible with segwitv0"); - Payload::WitnessProgram(prog) - } - - /// Create a pay to script payload that embeds a witness pay to script hash address - pub fn p2shwsh(script: &Script) -> Payload { - let ws = script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script(); - - Payload::ScriptHash(ws.script_hash()) - } - - /// Create a pay to taproot payload from untweaked key - pub fn p2tr( - secp: &Secp256k1, - internal_key: UntweakedPublicKey, - merkle_root: Option, - ) -> Payload { - let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root); - let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize()) - .expect("taproot output key has len 32 <= 40"); - Payload::WitnessProgram(prog) - } - - /// Create a pay to taproot payload from a pre-tweaked output key. - /// - /// This method is not recommended for use and [Payload::p2tr()] should be used where possible. - pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload { - let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize()) - .expect("taproot output key has len 32 <= 40"); - Payload::WitnessProgram(prog) - } - /// Returns a byte slice of the inner program of the payload. If the payload /// is a script hash or pubkey hash, a reference to the hash is returned. fn inner_prog_as_bytes(&self) -> &[u8] { match self { Payload::ScriptHash(hash) => hash.as_ref(), Payload::PubkeyHash(hash) => hash.as_ref(), - Payload::WitnessProgram(prog) => prog.program().as_bytes(), } } } @@ -648,20 +315,6 @@ impl<'a> fmt::Display for AddressEncoding<'a> { prefixed[1..].copy_from_slice(&hash[..]); base58::encode_check_to_fmt(fmt, &prefixed[..]) } - Payload::WitnessProgram(witness_prog) => { - let (version, prog) = (witness_prog.version(), witness_prog.program()); - let mut upper_writer; - let writer = if fmt.alternate() { - upper_writer = UpperWriter(fmt); - &mut upper_writer as &mut dyn fmt::Write - } else { - fmt as &mut dyn fmt::Write - }; - let mut bech32_writer = - bech32::Bech32Writer::new(self.bech32_hrp, version.bech32_variant(), writer)?; - bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?; - bech32::ToBase32::write_base32(&prog.as_bytes(), &mut bech32_writer) - } } } } @@ -792,10 +445,6 @@ struct AddressInner { /// /// * [BIP13 - Address Format for pay-to-script-hash](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki) /// * [BIP16 - Pay to Script Hash](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki) -/// * [BIP141 - Segregated Witness (Consensus layer)](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) -/// * [BIP142 - Address Format for Segregated Witness](https://github.com/bitcoin/bips/blob/master/bip-0142.mediawiki) -/// * [BIP341 - Taproot: SegWit version 1 spending rules](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) -/// * [BIP350 - Bech32m format for v1+ witness addresses](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] /// /// The `#[repr(transparent)]` attribute is used to guarantee that the layout of the @@ -907,9 +556,6 @@ impl bincode::Decode for AddressType { match val { 0 => Ok(AddressType::P2pkh), 1 => Ok(AddressType::P2sh), - 2 => Ok(AddressType::P2wpkh), - 3 => Ok(AddressType::P2wsh), - 4 => Ok(AddressType::P2tr), _ => Err(bincode::error::DecodeError::OtherString("invalid address type".to_string())), } } @@ -924,9 +570,6 @@ impl<'de, C> bincode::BorrowDecode<'de, C> for AddressType { match val { 0 => Ok(AddressType::P2pkh), 1 => Ok(AddressType::P2sh), - 2 => Ok(AddressType::P2wpkh), - 3 => Ok(AddressType::P2wsh), - 4 => Ok(AddressType::P2tr), _ => Err(bincode::error::DecodeError::OtherString("invalid address type".to_string())), } } @@ -967,25 +610,11 @@ impl Address { /// `address_type_internal`. /// /// # Returns - /// None if unknown, non-standard or related to the future witness version. + /// None if unknown or non-standard. fn address_type_internal(&self) -> Option { match self.payload() { Payload::PubkeyHash(_) => Some(AddressType::P2pkh), Payload::ScriptHash(_) => Some(AddressType::P2sh), - Payload::WitnessProgram(prog) => { - // BIP-141 p2wpkh or p2wsh addresses. - match prog.version() { - WitnessVersion::V0 => match prog.program().len() { - 20 => Some(AddressType::P2wpkh), - 32 => Some(AddressType::P2wsh), - _ => unreachable!( - "Address creation invariant violation: invalid program length" - ), - }, - WitnessVersion::V1 if prog.program().len() == 32 => Some(AddressType::P2tr), - _ => None, - } - } } } @@ -1037,7 +666,7 @@ impl Address { impl Address { /// Creates a pay to (compressed) public key hash address from a public key. /// - /// This is the preferred non-witness type address. + /// This is the standard address type for Dash. #[inline] pub fn p2pkh(pk: &PublicKey, network: Network) -> Address { Address::new(network, Payload::p2pkh(pk)) @@ -1052,75 +681,23 @@ impl Address { Ok(Address::new(network, Payload::p2sh(script)?)) } - /// Creates a witness pay to public key address from a public key. - /// - /// This is the native segwit address type for an output redeemable with a single signature. - /// - /// # Errors - /// Will only return an error if an uncompressed public key is provided. - pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result { - Ok(Address::new(network, Payload::p2wpkh(pk)?)) - } - - /// Creates a pay to script address that embeds a witness pay to public key. - /// - /// This is a segwit address type that looks familiar (as p2sh) to legacy clients. - /// - /// # Errors - /// Will only return an Error if an uncompressed public key is provided. - pub fn p2shwpkh(pk: &PublicKey, network: Network) -> Result { - Ok(Address::new(network, Payload::p2shwpkh(pk)?)) - } - - /// Creates a witness pay to script hash address. - pub fn p2wsh(script: &Script, network: Network) -> Address { - Address::new(network, Payload::p2wsh(script)) - } - - /// Creates a pay to script address that embeds a witness pay to script hash address. - /// - /// This is a segwit address type that looks familiar (as p2sh) to legacy clients. - pub fn p2shwsh(script: &Script, network: Network) -> Address { - Address::new(network, Payload::p2shwsh(script)) - } - - /// Creates a pay to taproot address from an untweaked key. - pub fn p2tr( - secp: &Secp256k1, - internal_key: UntweakedPublicKey, - merkle_root: Option, - network: Network, - ) -> Address { - Address::new(network, Payload::p2tr(secp, internal_key, merkle_root)) - } - - /// Creates a pay to taproot address from a pre-tweaked output key. - /// - /// This method is not recommended for use, [`Address::p2tr()`] should be used where possible. - pub fn p2tr_tweaked(output_key: TweakedPublicKey, network: Network) -> Address { - Address::new(network, Payload::p2tr_tweaked(output_key)) - } - /// Gets the address type of the address. /// /// # Returns - /// None if unknown, non-standard or related to the future witness version. + /// None if unknown or non-standard. #[inline] pub fn address_type(&self) -> Option { self.address_type_internal() } /// Checks whether or not the address is following Dash standardness rules when - /// *spending* from this address. *NOT* to be called by senders. + /// *spending* from this address. /// ///

/// Spending Standardness /// /// For forward compatibility, the senders must send to any [`Address`]. Receivers /// can use this method to check whether or not they can spend from this address. - /// - /// SegWit addresses with unassigned witness versions or non-standard program sizes are - /// considered non-standard. ///
/// pub fn is_spend_standard(&self) -> bool { @@ -1128,9 +705,6 @@ impl Address { } /// Checks whether or not the address is following Dash standardness rules. - /// - /// SegWit addresses with unassigned witness versions or non-standard program sizes are - /// considered non-standard. #[deprecated(since = "0.30.0", note = "Use Address::is_spend_standard instead")] pub fn is_standard(&self) -> bool { self.address_type().is_some() @@ -1174,37 +748,17 @@ impl Address { /// # assert_eq!(writer, ADDRESS); /// ``` pub fn to_qr_uri(&self) -> String { - let schema = match self.payload() { - Payload::WitnessProgram { - .. - } => "DASH", - _ => "dash", - }; - format!("{}:{:#}", schema, self) + format!("dash:{:#}", self) } /// Returns true if the given pubkey is directly related to the address payload. /// - /// This is determined by directly comparing the address payload with either the - /// hash of the given public key or the segwit redeem hash generated from the - /// given key. For taproot addresses, the supplied key is assumed to be tweaked + /// This is determined by directly comparing the address payload with the + /// hash of the given public key. pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool { let pubkey_hash = pubkey.pubkey_hash(); let payload = self.payload().inner_prog_as_bytes(); - let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner); - - (*pubkey_hash.as_byte_array() == *payload) - || (xonly_pubkey.serialize() == *payload) - || (*segwit_redeem_hash(&pubkey_hash).as_byte_array() == *payload) - } - - /// Returns true if the supplied xonly public key can be used to derive the address. - /// - /// This will only work for Taproot addresses. The Public Key is - /// assumed to have already been tweaked. - pub fn is_related_to_xonly_pubkey(&self, xonly_pubkey: &XOnlyPublicKey) -> bool { - let payload = self.payload().inner_prog_as_bytes(); - payload == xonly_pubkey.serialize() + *pubkey_hash.as_byte_array() == *payload } /// Returns true if the address creates a particular script @@ -1305,7 +859,7 @@ impl PartialEq
for Address { } } -impl From
for script::ScriptBuf { +impl From
for ScriptBuf { fn from(a: Address) -> Self { a.script_pubkey() } @@ -1336,42 +890,7 @@ impl FromStr for Address { type Err = Error; fn from_str(s: &str) -> Result, Error> { - // try bech32 - let bech32_network = match find_bech32_prefix(s) { - // note that upper or lowercase is allowed but NOT mixed case - "ds" | "DS" => Some(Network::Dash), - "td" | "TD" => Some(Network::Testnet), // this may also be a devnet - "dsrt" | "DSRT" => Some(Network::Regtest), - _ => None, - }; - if let Some(network) = bech32_network { - // decode as bech32 - let (_, payload, variant) = bech32::decode(s)?; - if payload.is_empty() { - return Err(Error::EmptyBech32Payload); - } - - // Get the script version and program (converted from 5-bit to 8-bit) - let (version, program): (WitnessVersion, Vec) = { - let (v, p5) = payload.split_at(1); - (WitnessVersion::try_from(v[0])?, bech32::FromBase32::from_base32(p5)?) - }; - - let witness_program = WitnessProgram::new(version, program)?; - - // Encoding check - let expected = version.bech32_variant(); - if expected != variant { - return Err(Error::InvalidBech32Variant { - expected, - found: variant, - }); - } - - return Ok(Address::new(network, Payload::WitnessProgram(witness_program))); - } - - // Base58 + // Dash only supports base58 addresses (P2PKH and P2SH) if s.len() > 50 { return Err(Error::Base58(base58::Error::InvalidLength(s.len() * 11 / 15))); } @@ -1400,71 +919,10 @@ impl FromStr for Address { } } -// // Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should -// // be used in QR codes, see [`Address::to_qr_uri`]. -// impl fmt::Display for Address { -// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -// let p2pkh_prefix = match self.network { -// Network::Dash => PUBKEY_ADDRESS_PREFIX_MAIN, -// Network::Testnet | Network::Devnet | Network::Regtest => PUBKEY_ADDRESS_PREFIX_TEST, -// }; -// let p2sh_prefix = match self.network { -// Network::Dash => SCRIPT_ADDRESS_PREFIX_MAIN, -// Network::Testnet | Network::Devnet | Network::Regtest => SCRIPT_ADDRESS_PREFIX_TEST, -// }; -// let bech32_hrp = match self.network { -// Network::Dash => "ds", -// Network::Testnet | Network::Devnet => "td", -// Network::Regtest => "dsrt", -// }; -// let encoding = AddressEncoding { -// payload: &self.payload, -// p2pkh_prefix, -// p2sh_prefix, -// bech32_hrp, -// }; -// encoding.fmt(fmt) -// } -// } - -struct UpperWriter(W); - -impl fmt::Write for UpperWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.chars() { - self.0.write_char(c.to_ascii_uppercase())?; - } - Ok(()) - } -} - -/// Extracts the bech32 prefix. -/// -/// # Returns -/// The input slice if no prefix is found. -fn find_bech32_prefix(bech32: &str) -> &str { - // Split at the last occurrence of the separator character '1'. - match bech32.rfind('1') { - None => bech32, - Some(sep) => bech32.split_at(sep).0, - } -} - -/// Convert a byte array of a pubkey hash into a segwit redeem hash -fn segwit_redeem_hash(pubkey_hash: &PubkeyHash) -> crate::hashes::hash160::Hash { - let mut sha_engine = sha256::Hash::engine(); - sha_engine.input(&[0, 20]); - sha_engine.input(pubkey_hash.as_ref()); - crate::hashes::hash160::Hash::from_engine(sha_engine) -} - #[cfg(test)] mod tests { use core::str::FromStr; - use hex_lit::hex; - use secp256k1::XOnlyPublicKey; - use super::*; use crate::crypto::key::PublicKey; use dash_network::Network::{Dash, Testnet}; @@ -1554,75 +1012,6 @@ mod tests { assert_eq!(Address::p2sh(&script, Testnet), Err(Error::ExcessiveScriptSize)); } - #[ignore] - #[test] - fn test_p2wpkh() { - // stolen from Dash transaction: b3c8c2b6cfc335abbcb2c7823a8453f55d64b2b5125a9a61e8737230cdb8ce20 - let mut key = "033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc" - .parse::() - .unwrap(); - let addr = Address::p2wpkh(&key, Dash).unwrap(); - assert_eq!(&addr.to_string(), "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw"); - assert_eq!(addr.address_type(), Some(AddressType::P2wpkh)); - roundtrips(&addr); - - // Test uncompressed pubkey - key.compressed = false; - assert_eq!(Address::p2wpkh(&key, Dash), Err(Error::UncompressedPubkey)); - } - - #[ignore] - #[test] - fn test_p2wsh() { - // stolen from Dash transaction 5df912fda4becb1c29e928bec8d64d93e9ba8efa9b5b405bd683c86fd2c65667 - let script = ScriptBuf::from_hex("52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae").unwrap(); - let addr = Address::p2wsh(&script, Dash); - assert_eq!( - &addr.to_string(), - "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej" - ); - assert_eq!(addr.address_type(), Some(AddressType::P2wsh)); - roundtrips(&addr); - } - - #[test] - fn test_p2shwpkh() { - // stolen from Dash transaction: ad3fd9c6b52e752ba21425435ff3dd361d6ac271531fc1d2144843a9f550ad01 - let mut key = "026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766" - .parse::() - .unwrap(); - let addr = Address::p2shwpkh(&key, Dash).unwrap(); - assert_eq!(&addr.to_string(), "7pu4bhf1eANQdvBoPH2FcDtDpMu4c7ZNuN"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); - roundtrips(&addr); - - // Test uncompressed pubkey - key.compressed = false; - assert_eq!(Address::p2wpkh(&key, Dash), Err(Error::UncompressedPubkey)); - } - - #[test] - fn test_p2shwsh() { - // stolen from Dash transaction f9ee2be4df05041d0e0a35d7caa3157495ca4f93b233234c9967b6901dacf7a9 - let script = ScriptBuf::from_hex("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap(); - let addr = Address::p2shwsh(&script, Dash); - assert_eq!(&addr.to_string(), "7WxUWa53KVEhSdBWwoB7LYMr2VedMZqoqt"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); - roundtrips(&addr); - } - - #[test] - fn test_non_existent_segwit_version() { - // 40-byte program - let program = hex!( - "654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4" - ); - let witness_prog = WitnessProgram::new(WitnessVersion::V13, program.to_vec()).unwrap(); - let addr = Address::new(Dash, Payload::WitnessProgram(witness_prog)); - roundtrips(&addr); - } - - #[ignore] #[test] fn test_address_debug() { // This is not really testing output of Debug but the ability and proper functioning @@ -1633,7 +1022,7 @@ mod tests { address: Address, } - let addr_str = "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"; + let addr_str = "7onv7GQcmoxJHdeDzTq2FGD9Q8Do9bxuFD"; let unchecked = Address::from_str(addr_str).unwrap(); assert_eq!( @@ -1672,103 +1061,6 @@ mod tests { } } - #[ignore] - #[test] - fn test_bip173_350_vectors() { - // Test vectors valid under both BIP-173 and BIP-350 - let valid_vectors = [ - ( - "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", - "0014751e76e8199196d454941c45d1b3a323f1433bd6", - ), - ( - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", - "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", - ), - ( - "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", - "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", - ), - ("BC1SW50QGDZ25J", "6002751e"), - ("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", "5210751e76e8199196d454941c45d1b3a323"), - ( - "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", - "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", - ), - ( - "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", - "5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", - ), - ( - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", - "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - ), - ]; - for vector in &valid_vectors { - let addr: Address = vector.0.parse::>().unwrap().assume_checked(); - assert_eq!(&addr.script_pubkey().to_hex_string(), vector.1); - roundtrips(&addr); - } - - let invalid_vectors = [ - // 1. BIP-350 test vectors - // Invalid human-readable part - "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", - // Invalid checksums (Bech32 instead of Bech32m): - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", - "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", - "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", - "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", - // Invalid character in checksum - "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", - // Invalid witness version - "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", - // Invalid program length (1 byte) - "bc1pw5dgrnzv", - // Invalid program length (41 bytes) - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", - // Invalid program length for witness version 0 (per BIP141) - "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", - // Mixed case - "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", - // zero padding of more than 4 bits - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", - // Non-zero padding in 8-to-5 conversion - "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", - // Empty data section - "bc1gmk9yu", - // 2. BIP-173 test vectors - // Invalid human-readable part - "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", - // Invalid checksum - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", - // Invalid witness version - "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", - // Invalid program length - "bc1rw5uspcuh", - // Invalid program length - "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", - // Invalid program length for witness version 0 (per BIP141) - "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", - // Mixed case - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", - // zero padding of more than 4 bits - "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", - // Non-zero padding in 8-to-5 conversion - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", - // Final test for empty data section is the same as above in BIP-350 - - // 3. BIP-173 valid test vectors obsolete by BIP-350 - "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", - "BC1SW50QA3JX3S", - "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", - ]; - for vector in &invalid_vectors { - assert!(vector.parse::>().is_err()); - } - } - #[test] #[cfg(feature = "serde")] #[ignore = "TODO: adjust tests for dash addr"] @@ -1872,22 +1164,9 @@ mod tests { Payload::PubkeyHash(PubkeyHash::all_zeros()), Payload::ScriptHash(ScriptHash::all_zeros()), ]; - let segwit_payload = (0..=16) - .map(|version| { - Payload::WitnessProgram( - WitnessProgram::new( - WitnessVersion::try_from(version).unwrap(), - vec![0xab; 32], // Choose 32 to make test case valid for all witness versions(including v0) - ) - .unwrap(), - ) - }) - .collect::>(); const LEGACY_EQUIVALENCE_CLASSES: &[&[Network]] = &[&[Network::Dash], &[Network::Testnet, Network::Regtest, Network::Devnet]]; - const SEGWIT_EQUIVALENCE_CLASSES: &[&[Network]] = - &[&[Network::Dash], &[Network::Regtest], &[Network::Testnet, Network::Devnet]]; fn test_addr_type(payloads: &[Payload], equivalence_classes: &[&[Network]]) { for pl in payloads { @@ -1914,67 +1193,6 @@ mod tests { } test_addr_type(legacy_payload, LEGACY_EQUIVALENCE_CLASSES); - test_addr_type(&segwit_payload, SEGWIT_EQUIVALENCE_CLASSES); - } - - #[test] - fn p2tr_from_untweaked() { - //Test case from BIP-086 - let internal_key = XOnlyPublicKey::from_str( - "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115", - ) - .unwrap(); - let secp = Secp256k1::verification_only(); - let address = Address::p2tr(&secp, internal_key, None, Network::Dash); - assert_eq!( - address.to_string(), - "ds1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqq9xzlq" - ); - assert_eq!(address.address_type(), Some(AddressType::P2tr)); - roundtrips(&address); - } - - #[ignore] - #[test] - fn test_is_related_to_pubkey_p2wpkh() { - let address_string = "bc1qhvd6suvqzjcu9pxjhrwhtrlj85ny3n2mqql5w4"; - let address = Address::from_str(address_string) - .expect("address") - .require_network(Network::Dash) - .expect("mainnet"); - - let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b"; - let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); - - let result = address.is_related_to_pubkey(&pubkey); - assert!(result); - - let unused_pubkey = PublicKey::from_str( - "02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c", - ) - .expect("pubkey"); - assert!(!address.is_related_to_pubkey(&unused_pubkey)) - } - - #[test] - fn test_is_related_to_pubkey_p2shwpkh() { - let address_string = "7fH3aFXJ5TWv5eNScx7m4FCf3XD27mXYHq"; - let address = Address::from_str(address_string) - .expect("address") - .require_network(Network::Dash) - .expect("mainnet"); - - let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b"; - let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); - - let result = address.is_related_to_pubkey(&pubkey); - assert!(result); - - let unused_pubkey = PublicKey::from_str( - "02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c", - ) - .expect("pubkey"); - assert!(!address.is_related_to_pubkey(&unused_pubkey)) } #[test] @@ -2019,77 +1237,20 @@ mod tests { assert!(!address.is_related_to_pubkey(&unused_pubkey)) } - #[ignore] - #[test] - fn test_is_related_to_pubkey_p2tr() { - let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b"; - let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); - let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner); - let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(xonly_pubkey); - let address = Address::p2tr_tweaked(tweaked_pubkey, Network::Dash); - - assert_eq!( - address, - Address::from_str("bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e") - .expect("address") - .require_network(Network::Dash) - .expect("mainnet") - ); - - let result = address.is_related_to_pubkey(&pubkey); - assert!(result); - - let unused_pubkey = PublicKey::from_str( - "02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c", - ) - .expect("pubkey"); - assert!(!address.is_related_to_pubkey(&unused_pubkey)); - } - - #[ignore] - #[test] - fn test_is_related_to_xonly_pubkey() { - let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b"; - let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); - let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner); - let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(xonly_pubkey); - let address = Address::p2tr_tweaked(tweaked_pubkey, Network::Dash); - - assert_eq!( - address, - Address::from_str("bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e") - .expect("address") - .require_network(Network::Dash) - .expect("mainnet") - ); - - let result = address.is_related_to_xonly_pubkey(&xonly_pubkey); - assert!(result); - } - #[test] fn test_fail_address_from_script() { - let bad_p2wpkh = ScriptBuf::from_hex("0014dbc5b0a8f9d4353b4b54c3db48846bb15abfec").unwrap(); - let bad_p2wsh = ScriptBuf::from_hex( - "00202d4fa2eb233d008cc83206fa2f4f2e60199000f5b857a835e3172323385623", - ) - .unwrap(); - let invalid_segwitv0_script = + // Test that unrecognized scripts fail + let unrecognized_script = ScriptBuf::from_hex("001161458e330389cd0437ee9fe3641d70cc18").unwrap(); let expected = Err(Error::UnrecognizedScript); - assert_eq!(Address::from_script(&bad_p2wpkh, Network::Dash), expected); - assert_eq!(Address::from_script(&bad_p2wsh, Network::Dash), expected); - assert_eq!( - Address::from_script(&invalid_segwitv0_script, Network::Dash), - Err(Error::InvalidSegwitV0ProgramLength(17)) - ); + assert_eq!(Address::from_script(&unrecognized_script, Network::Dash), expected); } #[test] fn valid_address_parses_correctly() { - let addr = AddressType::from_str("p2tr").expect("false negative while parsing address"); - assert_eq!(addr, AddressType::P2tr); + let addr = AddressType::from_str("p2pkh").expect("false negative while parsing address"); + assert_eq!(addr, AddressType::P2pkh); } #[test] @@ -2099,18 +1260,11 @@ mod tests { assert_eq!(got, want); } - #[ignore] #[test] fn test_matches_script_pubkey() { let addresses = [ - "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY", - "1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx", - "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k", - "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE", - "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", - "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw", - "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr", - "bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e", + "XgjvsEewx8SHii5SFYM856eU2qGrJuZ3AN", // P2PKH + "7fH3aFXJ5TWv5eNScx7m4FCf3XD27mXYHq", // P2SH ]; for addr in &addresses { let addr = Address::from_str(addr).unwrap().require_network(Network::Dash).unwrap(); diff --git a/dash/src/bip143.rs b/dash/src/bip143.rs deleted file mode 100644 index 703a0e8e6..000000000 --- a/dash/src/bip143.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Rust Dash Library -// Written in 2018 by -// Andrew Poelstra -// To the extent possible under law, the author(s) have dedicated all -// copyright and related and neighboring rights to this software to -// the public domain worldwide. This software is distributed without -// any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication -// along with this software. -// If not, see . -// - -//! BIP143 implementation. -//! -//! Implementation of BIP143 Segwit-style signatures. Should be sufficient -//! to create signatures for Segwit transactions (which should be pushed into -//! the appropriate place in the `Transaction::witness` array) or bcash -//! signatures, which are placed in the scriptSig. -//! - -use hashes::Hash; -use hash_types::Sighash; -use blockdata::script::Script; -use blockdata::witness::Witness; -use blockdata::transaction::{Transaction, hash_type::EcdsaSighashType}; -use consensus::{encode, Encodable}; - -use io; -use core::ops::{Deref, DerefMut}; -use blockdata::transaction::special_transaction::TransactionPayload; -use blockdata::transaction::txin::TxIn; -use sighash; - -/// Parts of a sighash which are common across inputs or signatures, and which are -/// sufficient (in conjunction with a private key) to sign the transaction -#[derive(Clone, PartialEq, Eq, Debug)] -#[deprecated(since = "0.24.0", note = "please use [sighash::SighashCache] instead")] -pub struct SighashComponents { - tx_version: u16, - tx_locktime: u32, - /// Hash of all the previous outputs - pub hash_prevouts: Sighash, - /// Hash of all the input sequence nos - pub hash_sequence: Sighash, - /// Hash of all the outputs in this transaction - pub hash_outputs: Sighash, - /// Transaction payload for special transactions - pub special_transaction_payload: Option, -} - -#[allow(deprecated)] -impl SighashComponents { - /// Compute the sighash components from an unsigned transaction and auxiliary - /// information about its inputs. - /// For the generated sighashes to be valid, no fields in the transaction may change except for - /// script_sig and witnesses. - pub fn new(tx: &Transaction) -> SighashComponents { - let hash_prevouts = { - let mut enc = Sighash::engine(); - for txin in &tx.input { - txin.previous_output.consensus_encode(&mut enc).expect("engines don't error"); - } - Sighash::from_engine(enc) - }; - - let hash_sequence = { - let mut enc = Sighash::engine(); - for txin in &tx.input { - txin.sequence.consensus_encode(&mut enc).expect("engines don't error"); - } - Sighash::from_engine(enc) - }; - - let hash_outputs = { - let mut enc = Sighash::engine(); - for txout in &tx.output { - txout.consensus_encode(&mut enc).expect("engines don't error"); - } - Sighash::from_engine(enc) - }; - - SighashComponents { - tx_version: tx.version, - tx_locktime: tx.lock_time, - hash_prevouts, - hash_sequence, - hash_outputs, - special_transaction_payload: tx.special_transaction_payload.clone(), - } - } - - /// Compute the BIP143 sighash for a `SIGHASH_ALL` signature for the given - /// input. - pub fn sighash_all(&self, txin: &TxIn, script_code: &Script, value: u64) -> Sighash { - let mut enc = Sighash::engine(); - self.tx_version.consensus_encode(&mut enc).expect("engines don't error"); - self.hash_prevouts.consensus_encode(&mut enc).expect("engines don't error"); - self.hash_sequence.consensus_encode(&mut enc).expect("engines don't error"); - txin - .previous_output - .consensus_encode(&mut enc) - .expect("engines don't error"); - script_code.consensus_encode(&mut enc).expect("engines don't error"); - value.consensus_encode(&mut enc).expect("engines don't error"); - txin.sequence.consensus_encode(&mut enc).expect("engines don't error"); - self.hash_outputs.consensus_encode(&mut enc).expect("engines don't error"); - self.tx_locktime.consensus_encode(&mut enc).expect("engines don't error"); - 1u32.consensus_encode(&mut enc).expect("engines don't error"); // hashtype - Sighash::from_engine(enc) - } -} - -/// A replacement for SigHashComponents which supports all sighash modes -#[deprecated(since = "0.28.0", note = "please use [sighash::SighashCache] instead")] -pub struct SigHashCache> { - cache: sighash::SighashCache, -} - -#[allow(deprecated)] -impl> SigHashCache { - /// Compute the sighash components from an unsigned transaction and auxiliary - /// in a lazy manner when required. - /// For the generated sighashes to be valid, no fields in the transaction may change except for - /// script_sig and witnesses. - pub fn new(tx: R) -> Self { - Self { cache: sighash::SighashCache::new(tx) } - } - - /// Encode the BIP143 signing data for any flag type into a given object implementing a - /// std::io::Write trait. - pub fn encode_signing_data_to( - &mut self, - writer: Write, - input_index: usize, - script_code: &Script, - value: u64, - sighash_type: EcdsaSighashType, - ) -> Result<(), encode::Error> { - self.cache - .segwit_encode_signing_data_to(writer, input_index, script_code, value, sighash_type) - .expect("input_index greater than tx input len"); - Ok(()) - } - - /// Compute the BIP143 sighash for any flag type. See SighashComponents::sighash_all simpler - /// API for the most common case - pub fn signature_hash( - &mut self, - input_index: usize, - script_code: &Script, - value: u64, - sighash_type: EcdsaSighashType - ) -> Sighash { - let mut enc = Sighash::engine(); - self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type) - .expect("engines don't error"); - Sighash::from_engine(enc) - } -} - -#[allow(deprecated)] -impl> SigHashCache { - /// When the SigHashCache is initialized with a mutable reference to a transaction instead of a - /// regular reference, this method is available to allow modification to the witnesses. - /// - /// This allows in-line signing such as - /// - /// panics if `input_index` is out of bounds with respect of the number of inputs - /// - /// ``` - /// use dashcore::blockdata::transaction::{Transaction, hash_type::EcdsaSighashType}; - /// use dashcore::bip143::SigHashCache; - /// use dashcore::Script; - /// - /// let mut tx_to_sign = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: Vec::new(), special_transaction_payload: None }; - /// let input_count = tx_to_sign.input.len(); - /// - /// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign); - /// for inp in 0..input_count { - /// let prevout_script = Script::new(); - /// let _sighash = sig_hasher.signature_hash(inp, &prevout_script, 42, EcdsaSighashType::All); - /// // ... sign the sighash - /// sig_hasher.access_witness(inp).push(&[]); - /// } - /// ``` - pub fn access_witness(&mut self, input_index: usize) -> &mut Witness { - self.cache.witness_mut(input_index).unwrap() - } -} - -#[cfg(test)] -#[allow(deprecated)] -mod tests { - use std::str::FromStr; - use hash_types::Sighash; - use blockdata::script::Script; - use blockdata::transaction::Transaction; - use consensus::encode::deserialize; - use network::constants::Network; - use address::Address; - use key::PublicKey; - use hashes::hex::FromHex; - - use super::*; - - fn p2pkh_hex(pk: &str) -> Script { - let pk: PublicKey = PublicKey::from_str(pk).unwrap(); - let witness_script = Address::p2pkh(&pk, Network::Dash).script_pubkey(); - witness_script - } - - fn run_test_sighash_bip143(tx: &str, script: &str, input_index: usize, value: u64, hash_type: u32, expected_result: &str) { - let tx: Transaction = deserialize(&Vec::::from_hex(tx).unwrap()[..]).unwrap(); - let script = Script::from(Vec::::from_hex(script).unwrap()); - let raw_expected = Sighash::from_hex(expected_result).unwrap(); - let expected_result = Sighash::from_slice(&raw_expected[..]).unwrap(); - let mut cache = SigHashCache::new(&tx); - let sighash_type = EcdsaSighashType::from_consensus(hash_type); - let actual_result = cache.signature_hash(input_index, &script, value, sighash_type); - assert_eq!(actual_result, expected_result); - } - - #[test] - fn bip143_p2wpkh() { - let tx = deserialize::( - &Vec::from_hex( - "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f000000\ - 0000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a01000000\ - 00ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093\ - 510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", - ).unwrap()[..], - ).unwrap(); - - let witness_script = p2pkh_hex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"); - let value = 600_000_000; - - let comp = SighashComponents::new(&tx); - assert_eq!( - comp, - SighashComponents { - tx_version: 1, - tx_locktime: 17, - hash_prevouts: hex_hash!( - Sighash, "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37" - ), - hash_sequence: hex_hash!( - Sighash, "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b" - ), - hash_outputs: hex_hash!( - Sighash, "863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5" - ), - special_transaction_payload: None - } - ); - - assert_eq!( - comp.sighash_all(&tx.input[1], &witness_script, value), - hex_hash!(Sighash, "313ca7f8ba3ad9da3eb040a61e2768eceb7126d610c13dbd440fdc3e1f1d07e0") - ); - } - - #[test] - fn bip143_p2wpkh_nested_in_p2sh() { - let tx = deserialize::( - &Vec::from_hex( - "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000\ - 0000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac00\ - 08af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000", - ).unwrap()[..], - ).unwrap(); - - let witness_script = p2pkh_hex("03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873"); - let value = 1_000_000_000; - let comp = SighashComponents::new(&tx); - assert_eq!( - comp, - SighashComponents { - tx_version: 1, - tx_locktime: 1170, - hash_prevouts: hex_hash!( - Sighash, "b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a" - ), - hash_sequence: hex_hash!( - Sighash, "18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198" - ), - hash_outputs: hex_hash!( - Sighash, "de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83" - ), - special_transaction_payload: None - } - ); - - assert_eq!( - comp.sighash_all(&tx.input[0], &witness_script, value), - hex_hash!(Sighash, "1c9c7380e80e8b12f62b896cb2a2f994c5f5d86ebb8b803bfdeab385ffd3a7a9") - ); - } - - #[test] - fn bip143_p2wsh_nested_in_p2sh() { - let tx = deserialize::( - &Vec::from_hex( - "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000\ - ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f\ - 05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000").unwrap()[..], - ).unwrap(); - - let witness_script = hex_script!( - "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28\ - bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b\ - 9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58\ - c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b1486\ - 2c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b\ - 56ae" - ); - let value = 987654321; - - let comp = SighashComponents::new(&tx); - assert_eq!( - comp, - SighashComponents { - tx_version: 1, - tx_locktime: 0, - hash_prevouts: hex_hash!( - Sighash, "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0" - ), - hash_sequence: hex_hash!( - Sighash, "3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044" - ), - hash_outputs: hex_hash!( - Sighash, "bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc" - ), - special_transaction_payload: None - } - ); - - assert_eq!( - comp.sighash_all(&tx.input[0], &witness_script, value), - hex_hash!(Sighash, "3a892568a19aee7bb185d5b4f7359e3e20d09db39ea38fdaf73eb719394fde69") - ); - } - #[test] - fn bip143_sighash_flags() { - // All examples generated via Bitcoin Core RPC using signrawtransactionwithwallet - // with additional debug printing - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x01, "8ada71798be05f37bcb5f4616f6bae8398f6f964d5a30f37b8602a2b9128d7d1"); - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x02, "9befbc916ed910f46e5426ed5aaff03e09f1574aedada0c330382eded78b24a6"); - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x03, "e500fc70ec0d42b895aaeca4aa4db352ed6876f9a12f1f7af4325bbef5442380"); - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x81, "10f6d9eff2a9ce5d70e45d24c9b1207cda90ae85d4dca987b6cf7e9429558c9b"); - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x82, "cef95d4dc84c2374657f4ae14cbf85217fc2c19dd38476c1751142e4f5522758"); - run_test_sighash_bip143("0200000001cf309ee0839b8aaa3fbc84f8bd32e9c6357e99b49bf6a3af90308c68e762f1d70100000000feffffff0288528c61000000001600146e8d9e07c543a309dcdeba8b50a14a991a658c5be0aebb0000000000160014698d8419804a5d5994704d47947889ff7620c004db000000", "76a91462744660c6b5133ddeaacbc57d2dc2d7b14d0b0688ac", 0, 1648888940, 0x83, "9c03ee1e7a7836cb92f86b80b6ad782e9debe40a56874b40f636a6c83aa40989"); - } -} diff --git a/dash/src/bip152.rs b/dash/src/bip152.rs index 62c35c1fe..187d50724 100644 --- a/dash/src/bip152.rs +++ b/dash/src/bip152.rs @@ -206,31 +206,10 @@ impl HeaderAndShortIds { last_prefill = idx + 1; prefilled.push(PrefilledTransaction { idx: diff_idx as u16, - tx: match version { - // > As encoded in "tx" messages sent in response to getdata MSG_TX - 1 => { - // strip witness for version 1 - let mut no_witness = tx.clone(); - no_witness.input.iter_mut().for_each(|i| i.witness.clear()); - no_witness - } - // > Transactions inside cmpctblock messages (both those used as direct - // > announcement and those in response to getdata) and in blocktxn should - // > include witness data, using the same format as responses to getdata - // > MSG_WITNESS_TX, specified in BIP144. - 2 => tx.clone(), - _ => unreachable!(), - }, + tx: tx.clone(), }); } else { - short_ids.push(ShortId::with_siphash_keys( - &match version { - 1 => tx.txid().to_raw_hash(), - 2 => tx.wtxid().to_raw_hash(), - _ => unreachable!(), - }, - siphash_keys, - )); + short_ids.push(ShortId::with_siphash_keys(&tx.txid().to_raw_hash(), siphash_keys)); } } @@ -376,7 +355,6 @@ impl BlockTransactions { #[cfg(test)] mod test { - use hashes::hex::FromHex; use super::*; use crate::blockdata::locktime::absolute; @@ -385,7 +363,6 @@ mod test { use crate::blockdata::transaction::outpoint::OutPoint; use crate::blockdata::transaction::txin::TxIn; use crate::blockdata::transaction::txout::TxOut; - use crate::blockdata::witness::Witness; use crate::consensus::encode::{deserialize, serialize}; use crate::hash_types::{TxMerkleNode, Txid}; use crate::pow::CompactTarget; @@ -398,7 +375,6 @@ mod test { previous_output: OutPoint::new(Txid::hash(nonce), 0), script_sig: ScriptBuf::new(), sequence: 1, - witness: Witness::new(), }], output: vec![TxOut { value: 1, @@ -442,20 +418,6 @@ mod test { assert_eq!(idxs, vec![0, 1]); } - #[test] - fn test_compact_block_vector() { - // Tested with Elements implementation of compact blocks. - let raw_block = Vec::::from_hex("000000206c750a364035aefd5f81508a08769975116d9195312ee4520dceac39e1fdc62c4dc67473b8e354358c1e610afeaff7410858bd45df43e2940f8a62bd3d5e3ac943c2975cffff7f200000000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04016b0101ffffffff020006062a0100000001510000000000000000266a24aa21a9ed4a3d9f3343dafcc0d6f6d4310f2ee5ce273ed34edca6c75db3a73e7f368734200120000000000000000000000000000000000000000000000000000000000000000000000000020000000001021fc20ba2bd745507b8e00679e3b362558f9457db374ca28ffa5243f4c23a4d5f00000000171600147c9dea14ffbcaec4b575e03f05ceb7a81cd3fcbffdffffff915d689be87b43337f42e26033df59807b768223368f189a023d0242d837768900000000171600147c9dea14ffbcaec4b575e03f05ceb7a81cd3fcbffdffffff0200cdf5050000000017a9146803c72d9154a6a20f404bed6d3dcee07986235a8700e1f5050000000017a9144e6a4c7cb5b5562904843bdf816342f4db9f5797870247304402205e9bf6e70eb0e4b495bf483fd8e6e02da64900f290ef8aaa64bb32600d973c450220670896f5d0e5f33473e5f399ab680cc1d25c2d2afd15abd722f04978f28be887012103e4e4d9312b2261af508b367d8ba9be4f01b61d6d6e78bec499845b4f410bcf2702473044022045ac80596a6ac9c8c572f94708709adaf106677221122e08daf8b9741a04f66a022003ccd52a3b78f8fd08058fc04fc0cffa5f4c196c84eae9e37e2a85babe731b57012103e4e4d9312b2261af508b367d8ba9be4f01b61d6d6e78bec499845b4f410bcf276a000000").unwrap(); - let raw_compact = Vec::::from_hex("000000206c750a364035aefd5f81508a08769975116d9195312ee4520dceac39e1fdc62c4dc67473b8e354358c1e610afeaff7410858bd45df43e2940f8a62bd3d5e3ac943c2975cffff7f2000000000a4df3c3744da89fa010a6979e971450100020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04016b0101ffffffff020006062a0100000001510000000000000000266a24aa21a9ed4a3d9f3343dafcc0d6f6d4310f2ee5ce273ed34edca6c75db3a73e7f368734200120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); - - let block: Block = deserialize(&raw_block).unwrap(); - let nonce = 18053200567810711460; - let compact = HeaderAndShortIds::from_block(&block, nonce, 2, &[]).unwrap(); - let compact_expected: HeaderAndShortIds = deserialize(&raw_compact).unwrap(); - - assert_eq!(compact, compact_expected); - } - #[test] fn test_getblocktx_differential_encoding_de_and_serialization() { let testcases = vec![ diff --git a/dash/src/blockdata/block.rs b/dash/src/blockdata/block.rs index 2036ada35..238293e2b 100644 --- a/dash/src/blockdata/block.rs +++ b/dash/src/blockdata/block.rs @@ -11,7 +11,7 @@ use core::fmt; -use hashes::{Hash, HashEngine}; +use hashes::Hash; use super::Weight; use crate::blockdata::script; @@ -19,7 +19,7 @@ use crate::blockdata::transaction::Transaction; use crate::consensus::{Decodable, Encodable, encode}; use crate::error::Error::{self, BlockBadProofOfWork, BlockBadTarget}; pub use crate::hash_types::BlockHash; -use crate::hash_types::{TxMerkleNode, WitnessCommitment, WitnessMerkleNode, Wtxid}; +use crate::hash_types::TxMerkleNode; use crate::internal_macros::impl_consensus_encoding; use crate::pow::{CompactTarget, Target, Work}; use crate::prelude::*; @@ -220,77 +220,12 @@ impl Block { } } - /// Checks if witness commitment in coinbase matches the transaction list. - pub fn check_witness_commitment(&self) -> bool { - const MAGIC: [u8; 6] = [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]; - // Witness commitment is optional if there are no transactions using SegWit in the block. - if self.txdata.iter().all(|t| t.input.iter().all(|i| i.witness.is_empty())) { - return true; - } - - if self.txdata.is_empty() { - return false; - } - - let coinbase = &self.txdata[0]; - if !coinbase.is_coin_base() { - return false; - } - - // Commitment is in the last output that starts with magic bytes. - if let Some(pos) = coinbase - .output - .iter() - .rposition(|o| o.script_pubkey.len() >= 38 && o.script_pubkey.as_bytes()[0..6] == MAGIC) - { - let commitment = WitnessCommitment::from_slice( - &coinbase.output[pos].script_pubkey.as_bytes()[6..38], - ) - .unwrap(); - // Witness reserved value is in coinbase input witness. - let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect(); - if witness_vec.len() == 1 - && witness_vec[0].len() == 32 - && let Some(witness_root) = self.witness_root() - { - return commitment - == Self::compute_witness_commitment(&witness_root, witness_vec[0]); - } - } - - false - } - /// Computes the transaction merkle root. pub fn compute_merkle_root(&self) -> Option { let hashes = self.txdata.iter().map(|obj| obj.txid().to_raw_hash()); merkle_tree::calculate_root(hashes).map(|h| h.into()) } - /// Computes the witness commitment for the block's transaction list. - pub fn compute_witness_commitment( - witness_root: &WitnessMerkleNode, - witness_reserved_value: &[u8], - ) -> WitnessCommitment { - let mut encoder = WitnessCommitment::engine(); - witness_root.consensus_encode(&mut encoder).expect("engines don't error"); - encoder.input(witness_reserved_value); - WitnessCommitment::from_engine(encoder) - } - - /// Computes the merkle root of transactions hashed for witness. - pub fn witness_root(&self) -> Option { - let hashes = self.txdata.iter().enumerate().map(|(i, t)| { - if i == 0 { - // Replace the first hash with zeroes. - Wtxid::all_zeros().to_raw_hash() - } else { - t.wtxid().to_raw_hash() - } - }); - merkle_tree::calculate_root(hashes).map(|h| h.into()) - } - /// base_size == size of header + size of encoded transaction count. fn base_size(&self) -> usize { 80 + VarInt(self.txdata.len() as u64).len() @@ -312,7 +247,7 @@ impl Block { /// Returns the weight of the block. pub fn weight(&self) -> Weight { - let base_weight = Weight::from_non_witness_data_size(self.base_size() as u64); + let base_weight = Weight::from_data_size(self.base_size() as u64); let txs_weight: Weight = self.txdata.iter().map(Transaction::weight).sum(); base_weight + txs_weight } @@ -489,13 +424,7 @@ mod tests { assert_eq!(real_decode.size(), some_block.len()); assert_eq!(real_decode.strippedsize(), some_block.len()); - assert_eq!( - real_decode.weight(), - Weight::from_non_witness_data_size(some_block.len() as u64) - ); - - // should be also ok for a non-witness block as commitment is optional in that case - assert!(real_decode.check_witness_commitment()); + assert_eq!(real_decode.weight(), Weight::from_data_size(some_block.len() as u64)); assert_eq!(serialize(&real_decode), some_block); } @@ -540,56 +469,11 @@ mod tests { assert_eq!(real_decode.size(), some_block.len()); assert_eq!(real_decode.strippedsize(), some_block.len()); - assert_eq!( - real_decode.weight(), - Weight::from_non_witness_data_size(some_block.len() as u64) - ); - - // should be also ok for a non-witness block as commitment is optional in that case - assert!(real_decode.check_witness_commitment()); + assert_eq!(real_decode.weight(), Weight::from_data_size(some_block.len() as u64)); assert_eq!(serialize(&real_decode), some_block); } - // Check testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b - #[ignore] - #[test] - fn segwit_block_test() { - let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw").to_vec(); - - let decode: Result = deserialize(&segwit_block); - - let prevhash = hex!("2aa2f2ca794ccbd40c16e2f3333f6b8b683f9e7179b2c4d74906000000000000"); - let merkle = hex!("10bc26e70a2f672ad420a6153dd0c28b40a6002c55531bfc99bf8994a8e8f67e"); - let work = Work::from(0x257c3becdacc64_u64); - - assert!(decode.is_ok()); - let real_decode = decode.unwrap(); - assert_eq!(real_decode.header.version, Version(Version::USE_VERSION_BITS as i32)); // VERSIONBITS but no bits set - assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash); - assert_eq!(serialize(&real_decode.header.merkle_root), merkle); - assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap()); - assert_eq!(real_decode.header.time, 1472004949); - assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(0x1a06d450)); - assert_eq!(real_decode.header.nonce, 1879759182); - assert_eq!(real_decode.header.work(), work); - assert_eq!( - real_decode.header.validate_pow(real_decode.header.target()).unwrap(), - real_decode.block_hash() - ); - assert_eq!(real_decode.header.difficulty(), 2456598); - assert_eq!(real_decode.header.difficulty_float(), 2456598.4399242126); - // [test] TODO: check the transaction data - - assert_eq!(real_decode.size(), segwit_block.len()); - assert_eq!(real_decode.strippedsize(), 4283); - assert_eq!(real_decode.weight(), Weight::from_wu(17168)); - - assert!(real_decode.check_witness_commitment()); - - assert_eq!(serialize(&real_decode), segwit_block); - } - #[test] fn block_version_test() { let block = hex!( @@ -660,10 +544,10 @@ mod tests { } } - let segwit_signal = Version(0x20000000 ^ 1 << 1); - assert!(!segwit_signal.is_signalling_soft_fork(0)); - assert!(segwit_signal.is_signalling_soft_fork(1)); - assert!(!segwit_signal.is_signalling_soft_fork(2)); + let bit1_signal = Version(0x20000000 ^ 1 << 1); + assert!(!bit1_signal.is_signalling_soft_fork(0)); + assert!(bit1_signal.is_signalling_soft_fork(1)); + assert!(!bit1_signal.is_signalling_soft_fork(2)); } } diff --git a/dash/src/blockdata/constants.rs b/dash/src/blockdata/constants.rs index 294006339..ec07a5cae 100644 --- a/dash/src/blockdata/constants.rs +++ b/dash/src/blockdata/constants.rs @@ -8,8 +8,6 @@ //! single transaction. //! -use core::default::Default; - use hashes::{Hash, sha256d}; use hex_lit::hex; @@ -21,7 +19,6 @@ use crate::blockdata::transaction::Transaction; use crate::blockdata::transaction::outpoint::OutPoint; use crate::blockdata::transaction::txin::TxIn; use crate::blockdata::transaction::txout::TxOut; -use crate::blockdata::witness::Witness; use crate::pow::CompactTarget; use dash_network::Network; @@ -33,12 +30,10 @@ pub const TARGET_BLOCK_SPACING: u32 = 600; pub const DIFFCHANGE_INTERVAL: u32 = 2016; /// How much time on average should occur between diffchanges. pub const DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600; -/// The maximum allowed weight for a block, see BIP 141 (network rule). +/// The maximum allowed weight for a block (network rule). pub const MAX_BLOCK_WEIGHT: u32 = 4_000_000; /// The minimum transaction weight for a valid serialized transaction. pub const MIN_TRANSACTION_WEIGHT: u32 = 4 * 60; -/// The factor that non-witness serialization data is multiplied by during weight calculation. -pub const WITNESS_SCALE_FACTOR: usize = 4; /// The maximum allowed number of signature check operations in a block. pub const MAX_BLOCK_SIGOPS_COST: i64 = 80_000; /// Mainnet (dash) pubkey address prefix. @@ -89,7 +84,6 @@ fn dash_genesis_tx() -> Transaction { previous_output: OutPoint::null(), script_sig: in_script, sequence: 0xFFFFFFFF, - witness: Witness::default(), }); // Outputs diff --git a/dash/src/blockdata/mod.rs b/dash/src/blockdata/mod.rs index 12cc342a7..bafcad2c6 100644 --- a/dash/src/blockdata/mod.rs +++ b/dash/src/blockdata/mod.rs @@ -15,7 +15,6 @@ pub mod opcodes; pub mod script; pub mod transaction; pub mod weight; -pub mod witness; pub use fee_rate::FeeRate; pub use weight::Weight; diff --git a/dash/src/blockdata/script/borrowed.rs b/dash/src/blockdata/script/borrowed.rs index 190ada4fe..1a19fafc5 100644 --- a/dash/src/blockdata/script/borrowed.rs +++ b/dash/src/blockdata/script/borrowed.rs @@ -1,14 +1,11 @@ // Written in 2014 by Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 -use core::convert::{TryFrom, TryInto}; use core::fmt; use core::ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; use hashes::Hash; -use secp256k1::{Secp256k1, Verification}; -use crate::address::WitnessVersion; use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::{self}; #[cfg(feature = "bitcoinconsensus")] @@ -17,11 +14,10 @@ use crate::blockdata::script::{ Builder, Instruction, InstructionIndices, Instructions, ScriptBuf, bytes_to_asm_fmt, }; use crate::consensus::Encodable; -use crate::hash_types::{ScriptHash, WScriptHash}; -use crate::key::{PublicKey, UntweakedPublicKey}; +use crate::hash_types::ScriptHash; +use crate::key::PublicKey; use crate::policy::DUST_RELAY_TX_FEE; use crate::prelude::*; -use crate::taproot::{LeafVersion, TapLeafHash, TapNodeHash}; /// Bitcoin script slice. /// @@ -132,18 +128,6 @@ impl Script { ScriptHash::hash(self.as_bytes()) } - /// Returns 256-bit hash of the script for P2WSH outputs. - #[inline] - pub fn wscript_hash(&self) -> WScriptHash { - WScriptHash::hash(self.as_bytes()) - } - - /// Computes leaf hash of tapscript. - #[inline] - pub fn tapscript_leaf_hash(&self) -> TapLeafHash { - TapLeafHash::from_script(self, LeafVersion::TapScript) - } - /// Returns the length in bytes of the script. #[inline] pub fn len(&self) -> usize { @@ -168,32 +152,6 @@ impl Script { Bytes(self.as_bytes().iter().copied()) } - /// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem - /// script"). - #[inline] - pub fn to_v0_p2wsh(&self) -> ScriptBuf { - ScriptBuf::new_v0_p2wsh(&self.wscript_hash()) - } - - /// Computes P2TR output with a given internal key and a single script spending path equal to - /// the current script, assuming that the script is a Tapscript. - #[inline] - pub fn to_v1_p2tr( - &self, - secp: &Secp256k1, - internal_key: UntweakedPublicKey, - ) -> ScriptBuf { - let leaf_hash = self.tapscript_leaf_hash(); - let merkle_root = TapNodeHash::from(leaf_hash); - ScriptBuf::new_v1_p2tr(secp, internal_key, Some(merkle_root)) - } - - /// Returns witness version of the script, if any, assuming the script is a `scriptPubkey`. - #[inline] - pub fn witness_version(&self) -> Option { - self.0.first().and_then(|opcode| WitnessVersion::try_from(opcodes::All::from(*opcode)).ok()) - } - /// Checks whether a script pubkey is a P2SH output. #[inline] pub fn is_p2sh(&self) -> bool { @@ -257,59 +215,6 @@ impl Script { } } - /// Checks whether a script pubkey is a Segregated Witness (segwit) program. - #[inline] - pub fn is_witness_program(&self) -> bool { - // A scriptPubKey (or redeemScript as defined in BIP16/P2SH) that consists of a 1-byte - // push opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new - // special meaning. The value of the first push is called the "version byte". The following - // byte vector pushed is called the "witness program". - let script_len = self.0.len(); - if !(4..=42).contains(&script_len) { - return false; - } - let ver_opcode = opcodes::All::from(self.0[0]); // Version 0 or PUSHNUM_1-PUSHNUM_16 - let push_opbyte = self.0[1]; // Second byte push opcode 2-40 bytes - WitnessVersion::try_from(ver_opcode).is_ok() - && push_opbyte >= OP_PUSHBYTES_2.to_u8() - && push_opbyte <= OP_PUSHBYTES_40.to_u8() - // Check that the rest of the script has the correct size - && script_len - 2 == push_opbyte as usize - } - - /// Checks whether a script pubkey is a P2WSH output. - #[inline] - pub fn is_v0_p2wsh(&self) -> bool { - self.0.len() == 34 - && self.witness_version() == Some(WitnessVersion::V0) - && self.0[1] == OP_PUSHBYTES_32.to_u8() - } - - /// Checks whether a script pubkey is a P2WPKH output. - #[inline] - pub fn is_v0_p2wpkh(&self) -> bool { - self.0.len() == 22 - && self.witness_version() == Some(WitnessVersion::V0) - && self.0[1] == OP_PUSHBYTES_20.to_u8() - } - - /// Returns P2WPKH byte slice if this script is P2WPKH, otherwise None. - pub fn v0_p2wpkh(&self) -> Option<&[u8; 20]> { - if self.is_v0_p2wpkh() { - Some(self.0[2..].try_into().expect("is_v0_p2wpkh checks the length")) - } else { - None - } - } - - /// Checks whether a script pubkey is a P2TR output. - #[inline] - pub fn is_v1_p2tr(&self) -> bool { - self.0.len() == 34 - && self.witness_version() == Some(WitnessVersion::V1) - && self.0[1] == OP_PUSHBYTES_32.to_u8() - } - /// Check if this is an OP_RETURN output. #[inline] pub fn is_op_return(&self) -> bool { @@ -336,17 +241,13 @@ impl Script { } /// Returns the minimum value an output with this script should have in order to be - /// broadcastable on today's Bitcoin network. + /// broadcastable on today's Dash network. pub fn dust_value(&self) -> crate::Amount { - // This must never be lower than Bitcoin Core's GetDustThreshold() (as of v0.21) as it may + // This must never be lower than Dash Core's GetDustThreshold() as it may // otherwise allow users to create transactions which likely can never be broadcast/confirmed. let sats = DUST_RELAY_TX_FEE as u64 / 1000 * // The default dust relay fee is 3000 satoshi/kB (i.e. 3 sat/vByte) if self.is_op_return() { 0 - } else if self.is_witness_program() { - 32 + 4 + 1 + (107 / 4) + 4 + // The spend cost copied from Core - 8 + // The serialized size of the TxOut's amount field - self.consensus_encode(&mut sink()).expect("sinks don't error") as u64 // The serialized size of this script_pubkey } else { 32 + 4 + 1 + 107 + 4 + // The spend cost copied from Core 8 + // The serialized size of the TxOut's amount field diff --git a/dash/src/blockdata/script/mod.rs b/dash/src/blockdata/script/mod.rs index 619d1a98a..011e003ae 100644 --- a/dash/src/blockdata/script/mod.rs +++ b/dash/src/blockdata/script/mod.rs @@ -64,7 +64,7 @@ use serde; use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::{self}; use crate::consensus::{Decodable, Encodable, encode}; -use crate::hash_types::{ScriptHash, WScriptHash}; +use crate::hash_types::ScriptHash; use crate::prelude::*; use crate::{OutPoint, io}; @@ -347,24 +347,6 @@ impl From<&Script> for ScriptHash { } } -impl From for WScriptHash { - fn from(script: ScriptBuf) -> WScriptHash { - script.wscript_hash() - } -} - -impl From<&ScriptBuf> for WScriptHash { - fn from(script: &ScriptBuf) -> WScriptHash { - script.wscript_hash() - } -} - -impl From<&Script> for WScriptHash { - fn from(script: &Script) -> WScriptHash { - script.wscript_hash() - } -} - impl AsRef