# Rust Taproot Transactions

This uses a bech32m encoded address and sends the amount from the UTXO to another bech32m address (- fees)\ <br>

{% code title="main.rs" lineNumbers="true" %}

```rust
use std::env;

use simple_wallet::p2tr_key::p2tr;
use structure::{constants::SEED, input_data::regtest_call::RegtestCall};

pub mod simple_wallet;
pub mod structure;

fn main() {
    env::set_var("RUST_BACKTRACE", "full");

    let client = RegtestCall::init(
        &vec!["bcrt1prnpxwf9tpjm4jll4ts72s2xscq66qxep6w9hf6sqnvwe9t4gvqasklfhyj"],
        "polar12_wallet",
        150,
    );

    p2tr(Some(SEED), client);
}
```

{% endcode %}

{% code title="constants.rs" lineNumbers="true" %}

```rust
pub const NETWORK: bitcoin::Network = bitcoin::Network::Regtest;
pub const TIP: u64 = 2000;
pub const SEED: &str = "1d454c6ab705f999d97e6465300a79a9595fb5ae1186ae20e33e12bea606c094";
pub const LOG: bool = true;
```

{% endcode %}

{% code title="regtest\_call.rs" lineNumbers="true" %}

```rust
use std::{collections::BTreeMap, str::FromStr};

use bitcoin::{Address, BlockHash, OutPoint, Script, Transaction, TxIn, Txid, Witness};
use bitcoincore_rpc::{
    bitcoincore_rpc_json::{ImportMultiResult, LoadWalletResult},
    jsonrpc::serde_json::{json, Map, Value},
    Client, RpcApi,
};

use super::RpcCall;

pub struct RegtestCall {
    amount: u64,
    tx_in: Vec<TxIn>,
    previous_tx: Vec<Transaction>,
    address_list: Vec<Address>,
    client: Client,
}

impl RpcCall for RegtestCall {
    fn contract_source(&self) -> Vec<Transaction> {
        return self.previous_tx.clone();
    }

    fn prev_input(&self) -> Vec<TxIn> {
        return self.tx_in.clone();
    }

    fn script_get_balance(&self) -> u64 {
        return self.amount.clone();
    }

    fn fee(&self) -> u64 {
        return 100000;
    }
    fn broadcasts_transacton(&self, tx: &Transaction) {
        let tx_id = RegtestCall::get_client().send_raw_transaction(tx).unwrap();
        println!("transaction send transaction id is: {}", tx_id)
    }
}

impl<'a> RegtestCall {
    pub fn get_client() -> Client {
        return Client::new(
            "http://127.0.0.1:18447",
            bitcoincore_rpc::Auth::UserPass("polaruser".to_string(), "polarpass".to_owned()),
        )
        .unwrap();
    }

    pub fn init(address_list: &Vec<&str>, wallet_name: &str, mine: u8) -> Self {
        let client = RegtestCall::get_client();

        address_list.iter().for_each(|address| {
            let mut addr = "addr(".to_owned();
            addr.push_str(&address);
            addr.push_str(")");

            client
                .get_descriptor_info(&addr)
                .map(|desc| {
                    let descriptor = desc.descriptor;
                    println!("assigned a descriptor {} ", descriptor);
                    create_wallet(&client, wallet_name, mine, &descriptor)
                })
                .unwrap();
        });
        return RegtestCall::from_string(address_list);
    }

    pub fn generatetodescriptor(
        client: &Client,
        block_num: u64,
        address: &Address,
    ) -> Vec<BlockHash> {
        return client.generate_to_address(block_num, address).unwrap();
    }

    pub fn transaction_broadcast(&self, tx: &Transaction) -> Txid {
        let tx_id = RegtestCall::get_client().send_raw_transaction(tx).unwrap();
        println!("transaction id: {}", &tx_id);
        return tx_id;
    }

    pub fn from_string(address_list: &'a Vec<&str>) -> RegtestCall {
        return RegtestCall::from_address(
            address_list
                .iter()
                .map(|addr| Address::from_str(addr).unwrap())
                .collect::<Vec<Address>>(),
        );
    }

    pub fn update(&self) -> Self {
        let tx_in = RegtestCall::get_txin(&self.client, &self.address_list).to_vec();
        let previous_tx = RegtestCall::get_previous_tx(&self.client, &tx_in);

        let amt = RegtestCall::get_amount(&previous_tx, &self.address_list);
        return RegtestCall {
            amount: amt,
            tx_in,
            previous_tx,
            address_list: self.address_list.clone(),
            client: RegtestCall::get_client(),
        };
    }

    fn get_txin(client: &Client, address_list: &Vec<Address>) -> Vec<TxIn> {
        return client
            .list_unspent(
                None,
                None,
                Some(&address_list.clone().iter().collect::<Vec<&Address>>()),
                None,
                None,
            )
            .unwrap()
            .iter()
            .map(|entry| {
                return TxIn {
                    previous_output: OutPoint::new(entry.txid, entry.vout),
                    script_sig: Script::new(),
                    sequence: bitcoin::Sequence(0xFFFFFFFF),
                    witness: Witness::default(),
                };
            })
            .collect::<Vec<TxIn>>();
    }

    fn get_previous_tx(client: &Client, tx_in: &Vec<TxIn>) -> Vec<Transaction> {
        return tx_in
            .iter()
            .map(|tx_id| {
                let result = client
                    .get_transaction(&tx_id.previous_output.txid, Some(true))
                    .unwrap()
                    .transaction()
                    .unwrap();
                return result;
            })
            .collect::<Vec<Transaction>>();
    }

    fn get_amount(previous_tx: &Vec<Transaction>, address_list: &Vec<Address>) -> u64 {
        return previous_tx
            .iter()
            .map(|tx| {
                tx.output
                    .iter()
                    .filter(|p| {
                        address_list
                            .clone()
                            .iter()
                            .map(|addr| addr.script_pubkey())
                            .collect::<Vec<Script>>()
                            .contains(&p.script_pubkey)
                    })
                    .map(|output_tx| output_tx.value)
                    .sum::<u64>()
            })
            .sum::<u64>();
    }

    pub fn from_address(address_list: Vec<Address>) -> Self {
        let client = RegtestCall::get_client();
        let tx_in = RegtestCall::get_txin(&client, &address_list).to_vec();
        let previous_tx = RegtestCall::get_previous_tx(&client, &tx_in);
        let amt = RegtestCall::get_amount(&previous_tx, &address_list);
        return RegtestCall {
            amount: amt,
            tx_in,
            previous_tx,
            address_list,
            client,
        };
    }
}

fn create_wallet(client: &Client, wallet_name: &str, mine: u8, desc: &String) {
    if client
        .list_wallets()
        .unwrap()
        .contains(&wallet_name.to_owned())
    {
        importdescriptors(client, desc, mine);
        return;
    }
    client
        .create_wallet(wallet_name, Some(true), Some(true), None, Some(false))
        .map(|load_wallet| {
            println!("wallet {} created successfully ", load_wallet.name);

            if let Some(msg) = load_wallet.warning {
                println!("Warning! {}", msg)
            }

            println!("wallet {} created successfully ", load_wallet.name);

            importdescriptors(client, desc, mine);
        })
        .unwrap();
}

fn importdescriptors(client: &Client, desc: &String, mine: u8) {
    let mut params = Map::new();
    params.insert("desc".to_owned(), Value::String(desc.to_string()));
    params.insert("timestamp".to_owned(), Value::String("now".to_owned()));

    client
        .call::<Vec<ImportMultiResult>>(
            "importdescriptors",
            &[Value::Array([Value::Object(params)].to_vec())],
        )
        .map(|import| {
            import.iter().for_each(|result| {
                if result.success {
                    println!("descriptor successfully imported");
                }
                result.error.iter().for_each(|err| {
                    panic!("error importing wallet {:#?}", err);
                })
            });

            mine_to_descriptors(client, mine, desc);
        })
        .unwrap()
}

fn mine_to_descriptors(client: &Client, mine: u8, desc: &String) {
    client
        .call::<Vec<BlockHash>>("generatetodescriptor", &[json!(mine), json!(desc)])
        .unwrap();
    println!("successfully mined blocks");
}
```

{% endcode %}

{% code title="p2tr\_keys" lineNumbers="true" %}

```rust
use std::str::FromStr;

use bitcoin::{
    psbt::{Input, PartiallySignedTransaction, Prevouts},
    schnorr::TapTweak,
    secp256k1::{All, Message, Scalar, Secp256k1, SecretKey},
    util::{
        bip32::{ExtendedPrivKey, ExtendedPubKey},
        sighash::SighashCache,
    },
    Address, KeyPair, PackedLockTime, SchnorrSig, Transaction, TxOut,
};
use miniscript::psbt::PsbtExt;

use crate::structure::{
    constants::NETWORK,
    input_data::{regtest_call::RegtestCall, RpcCall},
};

pub fn p2tr(secret_string: Option<&str>, client: impl RpcCall) {
    let secp = Secp256k1::new();
    let secret = match secret_string {
        Some(sec_str) => SecretKey::from_str(&sec_str).unwrap(),
        None => {
            let scalar = Scalar::random();
            let secret_key = SecretKey::from_slice(&scalar.to_be_bytes()).unwrap();
            println!("secret_key: {}", secret_key.display_secret());
            secret_key
        }
    };

    let key_pair = KeyPair::from_secret_key(&secp, &secret);

    let (x_only, _) = key_pair.x_only_public_key();

    let address = Address::p2tr(&secp, x_only, None, NETWORK);

    println!("address {}", address.to_string());

    let ext_pub = ExtendedPubKey::from_priv(
        &secp,
        &ExtendedPrivKey::new_master(NETWORK, &secret.secret_bytes()).unwrap(),
    );

    println!("xpub {}", ext_pub.to_string());

    if (secret_string.is_none()) {
        return;
    }

    let tx_in_list = client.prev_input();

    let transaction_list = client.contract_source();

    let prevouts = transaction_list
        .iter()
        .flat_map(|tx| tx.output.clone())
        .filter(|p| address.script_pubkey().eq(&p.script_pubkey))
        .collect::<Vec<TxOut>>();

    let total: u64 = prevouts.iter().map(|tx_out| tx_out.value).sum();

    let out_put = create_output(total, &client);

    let unsigned_tx = Transaction {
        version: 2,
        lock_time: PackedLockTime(0),
        input: tx_in_list,
        output: out_put,
    };

    let mut psbt = PartiallySignedTransaction::from_unsigned_tx(unsigned_tx.clone()).unwrap();
    psbt.inputs = sign_all_unsigned_tx(&secp, &prevouts, &unsigned_tx, &key_pair);

    let tx = psbt.finalize(&secp).unwrap().extract_tx();

    client.broadcasts_transacton(&tx);
}

fn create_output<'a>(total: u64, client: &'a impl RpcCall) -> Vec<TxOut> {
    let out_put = vec![TxOut {
        value: total - client.fee(),
        script_pubkey: Address::from_str(
            "bcrt1prnpxwf9tpjm4jll4ts72s2xscq66qxep6w9hf6sqnvwe9t4gvqasklfhyj",
        )
        .unwrap()
        .script_pubkey(),
    }];
    out_put
}

fn sign_all_unsigned_tx(
    secp: &Secp256k1<All>,
    prevouts: &Vec<TxOut>,
    unsigned_tx: &Transaction,
    key_pair: &KeyPair,
) -> Vec<Input> {
    return prevouts
        .iter()
        .enumerate()
        .map(|(index, tx_out)| {
            sign_tx(secp, index, unsigned_tx, &prevouts, key_pair, tx_out).clone()
        })
        .collect();
}

fn sign_tx(
    secp: &Secp256k1<All>,
    index: usize,
    unsigned_tx: &Transaction,
    prevouts: &Vec<TxOut>,
    key_pair: &KeyPair,
    tx_out: &TxOut,
) -> Input {
    let sighash = SighashCache::new(&mut unsigned_tx.clone())
        .taproot_key_spend_signature_hash(
            index,
            &Prevouts::All(&prevouts),
            bitcoin::SchnorrSighashType::AllPlusAnyoneCanPay,
        )
        .unwrap();

    let message = Message::from_slice(&sighash).unwrap();

    let tweaked_key_pair = key_pair.tap_tweak(&secp, None);

    let sig = secp.sign_schnorr(&message, &tweaked_key_pair.to_inner());

    secp.verify_schnorr(
        &sig,
        &message,
        &tweaked_key_pair.to_inner().x_only_public_key().0,
    )
    .unwrap();

    let schnorr_sig = SchnorrSig {
        sig,
        hash_ty: bitcoin::SchnorrSighashType::AllPlusAnyoneCanPay,
    };

    let mut input = Input::default();

    input.witness_script = Some(tx_out.script_pubkey.clone());

    input.tap_key_sig = Some(schnorr_sig);

    input.witness_utxo = Some(tx_out.clone());

    return input;
}
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.degenlab.io/decentralized-mining-pool/rust-taproot-transactions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
