diff --git a/2.1-Segwit-Version-1.ipynb b/2.1-Segwit-Version-1.ipynb new file mode 100644 index 000000000..5a89eb441 --- /dev/null +++ b/2.1-Segwit-Version-1.ipynb @@ -0,0 +1,580 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import hashlib\n", + "from io import BytesIO\n", + "import random\n", + "\n", + "import util\n", + "from test_framework.address import program_to_witness\n", + "from test_framework.key import ECKey, ECPubKey, generate_schnorr_nonce\n", + "from test_framework.messages import CTransaction, COutPoint, CTxIn, CTxOut, CScriptWitness, CTxInWitness\n", + "from test_framework.musig import generate_musig_key, aggregate_schnorr_nonces, sign_musig, aggregate_musig_signatures\n", + "from test_framework.script import CScript, OP_1, TaprootSignatureHash\n", + "import test_framework.segwit_addr as segwit_addr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2.1 Taproot Outputs\n", + "\n", + "* Example: spending from a segwit v1 single-key output\n", + " * Part 1: Generating a segwit v1 output address\n", + " * Part 2: Sending funds from the Bitcoin Core wallet\n", + " * Part 3: Constructing a transaction to spend the segwit v1 output\n", + "* Exercise: spending from a segwit v1 musig aggregate pubkey output\n", + "\n", + "In this chapter, we introduce segwit v1 outputs, which are defined in [bip-taproot](https://github.com/bitcoinops/bips/blob/v0.1/bip-taproot.mediawiki). Segwit v1 outputs can be spent in two ways:\n", + "\n", + "* **Key path** spending, which treats the witness program as a public key, and permits spending using a signature from that public key.\n", + "* **Script path** spending, which allows a pre-committed script to be used to spend the output. Script path spending will be fully described in chapters 2.2, 2.3 and 2.4.\n", + "\n", + "By using the MuSig pubkey and signature aggregation protocol described in chapter 1.2, key path spending can be used to encumber an output to an n-of-n multisig policy in a way that is indistinguishable from a single-key output and spend.\n", + "\n", + "The first half of this chapter shows an example of sending funds to a segwit v1 address using the Bitcoin Core wallet, and then manually constructing a transaction that spends that output using the new bip-taproot key path spending rules.\n", + "\n", + "There is then a coding exercise to send funds to a MuSig aggregate pubkey address, and then construct a transaction that spends from that address using the bip-taproot key path spending rules." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 1. Generating a segwit v1 output\n", + "\n", + "Segwit v1 follows the same output script pattern as Segwit V0:\n", + "\n", + "* Segwit output: **`[1B Version]` `[segwit program]`**\n", + "* Segwit v0 output: **`[00]` `[20-Byte public key digest]`** (P2WPKH) or **`[00]` `[32-Byte script digest]`** (P2WSH)\n", + "* Segwit v1 output: **`[01]` `[33-Byte public key]`**\n", + "\n", + "The 33-Byte public key encoding is similar to that of legacy compressed pubkeys, but with a different oddness byte.\n", + "\n", + "* Y-coordinate: even - **`[00]`** or odd - **`[01]`**\n", + "* X-coordinate: **`[32B x-coordinate]`**\n", + "* 33 byte pubkey encoding: **`[1B oddness] [32B x-coordinate]`**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Spending a segwit v1 output with the key path\n", + "\n", + "Unlike Segwit v0 outputs, v1 outputs look the same for script or key paths(unlike v0 which separates into P2WPKH and P2WSH). In this chapter we will focus on spending the key path.\n", + "\n", + "![test](images/segwit_version1_0.jpg)\n", + "\n", + "The output can be spent along the **key path** by providing a valid signature for the pubkey in the output's scriptPubKey. The spending witness is simply **`[sig]`**.\n", + "\n", + "The output can be spent along the **script path** if public key was tweaked with a valid taproot. See chapters 2.2 and 2.3 for further details." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.1 Example: Constructing a segwit v1 output\n", + "\n", + "In this example, we construct segwit v1 output for spending along the key path. We generate a key pair, encode the public key using the bip-schnorr and bip-taproot pubkey encoding rules, and then encode the witness version and witness program to a bech32 address." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Key pair generation\n", + "privkey = ECKey().generate()\n", + "pubkey = privkey.get_pubkey()\n", + "print(\"Pubkey is {}\\n\".format(pubkey.get_bytes().hex()))\n", + "\n", + "# Create Segwit program ([1B oddness] [32B x-coordinate])\n", + "pubkey_data = pubkey.get_bytes()\n", + "program = bytes([pubkey_data[0] & 1]) + pubkey_data[1:]\n", + "print(\"Segwit program is {}\\n\".format(program.hex()))\n", + "\n", + "# Create (regtest) bech32 address\n", + "version = 0x01\n", + "address = segwit_addr.encode(\"bcrt\", version, program)\n", + "print(\"bech32 address is {}\".format(address))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2. Sending funds from the Bitcoin Core wallet\n", + "\n", + "Next, we send funds to the segwit v1 address that we just generated. We'll create send the funds from a Bitcoin Core wallet, which is able to send outputs to segwit v1 addresses." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.2: Startup TestWrapper to initialize a regtest node and wallet\n", + "* Run setup only once, or after a clean shutdown." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test = util.TestWrapper()\n", + "test.setup(num_nodes=1)\n", + "\n", + "version = test.nodes[0].getnetworkinfo()['subversion']\n", + "print(\"Client version is {}\".format(version))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.3: Generate coins for the wallet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test.nodes[0].generate(101)\n", + "balance = test.nodes[0].getbalance()\n", + "print('Balance: {}'.format(balance))\n", + "\n", + "assert balance > 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.4: Send funds from the Bitcoin Core wallet to the segwit address" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Send wallet transaction to segwit address\n", + "amount_btc = 0.05\n", + "txid = test.nodes[0].sendtoaddress(address, amount_btc)\n", + "\n", + "# Decode wallet transaction\n", + "tx_hex = test.nodes[0].getrawtransaction(txid) \n", + "decoded_tx = test.nodes[0].decoderawtransaction(tx_hex)\n", + "\n", + "print(\"Transaction:\\n{}\\n\".format(decoded_tx))\n", + "\n", + "# Reconstruct wallet transaction locally\n", + "tx = CTransaction()\n", + "tx.deserialize(BytesIO(bytes.fromhex(tx_hex)))\n", + "tx.rehash()\n", + "\n", + "# We can check if the transaction was correctly deserialized\n", + "assert txid == decoded_tx[\"txid\"]\n", + "\n", + "# The wallet randomizes the change output index for privacy\n", + "# Loop through the outputs and return the first where the scriptPubKey matches the segwit v1 output\n", + "output_index, output = next(out for out in enumerate(tx.vout) if out[1].scriptPubKey == CScript([OP_1, program]))\n", + "\n", + "print(\"Segwit v1 output is {}\".format(output))\n", + "print(\"Segwit v1 output value is {}\".format(output.nValue))\n", + "print(\"Segwit v1 output index is {}\".format(output_index))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 3. Constructing a transaction to spend the segwit v1 output\n", + "\n", + "We are now going to manually contruct, sign and broadcast a transaction which spends the segwit v1 output.\n", + "\n", + "To do that we create a `CTransaction` object and populate the data members:\n", + "\n", + " * `nVersion`\n", + " * `nLocktime` \n", + " * `tx_vin` (list of `CTxIn` objects)\n", + " * `tx_vout` (list of `CTxOut` objects)\n", + " * `tx.wit.vtxinwit` (list of `CTxInWitness` objects)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.5: Construct `CTransaction` and populate inputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Construct transaction\n", + "spending_tx = CTransaction()\n", + "\n", + "# Populate the transaction version\n", + "spending_tx.nVersion = 1\n", + "\n", + "# Populate the locktime\n", + "spending_tx.nLockTime = 0\n", + "\n", + "# Populate the transaction inputs\n", + "outpoint = COutPoint(tx.sha256, output_index)\n", + "spending_tx_in = CTxIn(outpoint = outpoint)\n", + "spending_tx.vin = [spending_tx_in]\n", + "\n", + "print(\"Spending transaction:\\n{}\".format(spending_tx))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.6: Populate outputs\n", + "\n", + "We'll generate an output address in the Bitcoin Core wallet to send the funds to, determine the fee, and then populate the spending_tx with an output to that address." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate new Bitcoin Core wallet address\n", + "dest_addr = test.nodes[0].getnewaddress(address_type=\"bech32\")\n", + "scriptpubkey = bytes.fromhex(test.nodes[0].getaddressinfo(dest_addr)['scriptPubKey'])\n", + "\n", + "# Determine minimum fee required for mempool acceptance\n", + "min_fee = int(test.nodes[0].getmempoolinfo()['mempoolminfee'] * 100000000)\n", + "\n", + "# Complete output which returns funds to Bitcoin Core wallet\n", + "amount_sat = int(amount_btc * 100000000)\n", + "dest_output = CTxOut(nValue=amount_sat - min_fee, scriptPubKey=scriptpubkey)\n", + "spending_tx.vout = [dest_output]\n", + "\n", + "print(\"Spending transaction:\\n{}\".format(spending_tx))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.7: Sign the transaction with a schnorr signature\n", + "\n", + "bip-taproot defines the following sighash flags:\n", + "* Legacy sighash flags:\n", + " * `0x01` - **SIGHASH_ALL**\n", + " * `0x02` - **SIGHASH_NONE**\n", + " * `0x03` - **SIGHASH_SINGLE**\n", + " * `0x81` - **SIGHASH_ALL | SIGHASH_ANYONECANPAY**\n", + " * `0x82` - **SIGHASH_NONE | SIGHASH_ANYONECANPAY**\n", + " * `0x83` - **SIGHASH_SINGLE | SIGHASH_ANYONECANPAY**\n", + "* New sighash flag:\n", + " * `0x00` - same semantics `0x01` **SIGHASH_ALL**\n", + "\n", + "Append the sighash flag to the signature `[R_x, s]` with the sighash byte if not `0x00`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate the Taproot Signature Hash for signing\n", + "hash_types = [0,1,2,3,0x81,0x82,0x83]\n", + "sighash = TaprootSignatureHash(spending_tx, [output], hash_types[0], input_index = 0, scriptpath = False)\n", + " \n", + "# All schnorr sighashes except SIGHASH_ALL require\n", + "# the hash_type appended to the end of signature\n", + "sig = privkey.sign_schnorr(sighash)\n", + "\n", + "print(\"Signature: {}\".format(sig.hex()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.1.8: Add the witness and test acceptance of the transaction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Construct transaction witness\n", + "witness = CScriptWitness()\n", + "witness.stack.append(sig)\n", + "witness_in = CTxInWitness()\n", + "witness_in.scriptWitness = witness\n", + "\n", + "# vtxinwit is a list of the witness data(i.e. signatures etc.)\n", + "spending_tx.wit.vtxinwit.append(witness_in)\n", + "\n", + "print(\"Spending transaction:\\n{}\\n\".format(spending_tx))\n", + " \n", + "# Serialize signed transaction for broadcast\n", + "spending_tx_str = spending_tx.serialize().hex()\n", + " \n", + "# Test mempool acceptance\n", + "assert test.nodes[0].testmempoolaccept([spending_tx_str])[0]['allowed']\n", + "print(\"Success!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Shutdown the TestWrapper (and all bitcoind instances)**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test.shutdown()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise: Spending a segwit v1 output with a MuSig public key\n", + "\n", + "In this exercise, we'll use the Bitcoin Core wallet to create a transaction that sends two outputs. One to a 2-of-2 musig aggregate pubkey (segwit v1 address), and one to a (single) public key (segwit v1 address).\n", + "\n", + "We'll then manually create a transaction that spends the musig output and sends an output back to the Bitcoin Core wallet. We'll use Bitcoin Core's `testmempoolaccept()` RPC method to verify that the transaction is valid." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Start the TestWrapper (and bitcoind node)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test = util.TestWrapper()\n", + "test.setup()\n", + "\n", + "version = test.nodes[0].getnetworkinfo()['subversion']\n", + "print(\"Client version is {}\".format(version))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.9 _Programming Exercise:_ Generate segwit v1 addresses for a MuSig aggregate pubkey and a single-key" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate individual key pairs\n", + "privkey1 = ECKey().generate()\n", + "privkey2 = ECKey().generate()\n", + "privkey3 = ECKey().generate()\n", + "pk1 = privkey1.get_pubkey()\n", + "pk2 = privkey2.get_pubkey()\n", + "pk3 = privkey3.get_pubkey()\n", + "\n", + "# Generate MuSig key\n", + "\n", + "\n", + "# Multiply individual keys with challenges\n", + "\n", + "\n", + "# Create a segwit v1 address for the MuSig aggregate pubkey\n", + "# Use segwit_addr.encode(\"bcrt\", witness_version, witness_program)\n", + "program_musig = # TODO: implement\n", + "address_musig = # TODO: implement\n", + "print(\"2-of-2 musig: \", address_musig)\n", + "\n", + "# Create segwit v1 address (single)\n", + "program_single = # TODO: implement\n", + "address_single = # TODO: implement\n", + "print(\"Single key: \", address_single)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.10 Create a transaction in the Bitcoin Core wallet sending outputs to the segwit v1 addresses" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate coins in Bitcoin Core wallet\n", + "blocks = test.nodes[0].generate(101)\n", + "balance = test.nodes[0].getbalance()\n", + "print('Balance:', balance)\n", + "\n", + "# Create wallet transaction sending to segwit v1 addresses (address_musig, address_single)\n", + "amount_btc = 0.05\n", + "txid = test.nodes[0].sendmany(amounts={address_musig: amount_btc, address_single: 0.05})\n", + "\n", + "# Reconstruct wallet transactions locally\n", + "tx_hex = test.nodes[0].getrawtransaction(txid)\n", + "\n", + "tx = CTransaction()\n", + "tx.deserialize(BytesIO(bytes.fromhex(tx_hex)))\n", + "tx.rehash()\n", + "\n", + "print(\"Transaction:\\n{}\\n\".format(tx))\n", + "\n", + "# Loop through outputs and find the MuSig output\n", + "output_index, output = next(out for out in enumerate(tx.vout) if out[1].scriptPubKey == CScript([OP_1, program_musig]))\n", + "\n", + "print(\"MuSig output is {}\".format(output))\n", + "print(\"MuSig output value is {}\".format(output.nValue))\n", + "print(\"MuSig output index is {}\".format(output_index))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.11 _Programming Exercise:_ Instantiate a CTransaction object and populate the version, locktime and inputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Construct transaction which spends the musig segwit v1 output\n", + "spending_tx = CTransaction()\n", + "spending_tx.nVersion = # TODO: implement\n", + "spending_tx.nLockTime = # TODO: implement\n", + "spending_tx.vin = # TODO: implement\n", + "\n", + "# Generate new Bitcoin Core wallet address\n", + "dest_addr = # TODO: implement\n", + "scriptpubkey = # TODO: implement\n", + "print(\"Destination address: {}\\n\".format(dest_addr))\n", + "\n", + "# Determine minimum fee required for mempool acceptance\n", + "min_fee = int(test.nodes[0].getmempoolinfo()['mempoolminfee'] * 100000000)\n", + "\n", + "# Complete output which returns funds to Bitcoin Core wallet\n", + "spending_tx.vout = # TODO: implement\n", + "print(\"Spending transaction:\\n{}\\n\".format(spending_tx))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.12 _Programming Exercise:_ Create a valid bip-schnorr signature for the MuSig aggregate pubkey" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create sighash for ANY|ALL\n", + "sighash_musig = # TODO: implement\n", + " \n", + "# Generate individual nonces for participants and an aggregate nonce point\n", + "# Remember to negate the individual nonces if necessary\n", + "R_agg # TODO: implement\n", + "\n", + "# Create an aggregate signature\n", + "sig_agg = # TODO: implement\n", + "\n", + "print(\"Aggregate signature is {}\".format(sig_agg.hex()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.1.13 _Programming Exercise:_ Construct the witness, add it to the transaction and verify mempool acceptance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Construct transaction witness\n", + "witness = CScriptWitness()\n", + "witness.stack.append(sig_agg)\n", + "witness_in = CTxInWitness()\n", + "witness_in.scriptWitness = witness\n", + "spending_tx.wit.vtxinwit.append(witness_in)\n", + " \n", + "# Serialize Schnorr transaction for broadcast\n", + "spending_tx_str = spending_tx.serialize().hex()\n", + " \n", + "# Test mempool acceptance\n", + "assert test.nodes[0].testmempoolaccept([spending_tx_str])[0]['allowed']\n", + "print(\"Success!\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/images/segwit_version1_0.jpg b/images/segwit_version1_0.jpg new file mode 100644 index 000000000..7b212be78 Binary files /dev/null and b/images/segwit_version1_0.jpg differ diff --git a/images/segwit_version1_0.svg b/images/segwit_version1_0.svg new file mode 100644 index 000000000..06b68f43f --- /dev/null +++ b/images/segwit_version1_0.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + Segwit V1 Output Spending Paths + + + + [01] [33B Public Key] + + + + [64/65B signature] + + + + [initial stack][tapscript][controlblock] + + + + Segwit v1 output: + + + + Key Path Spending: + + + + Script Path Spending: + + + + + + + + + diff --git a/solutions/2.1-Segwit-Version-1-Solutions.txt b/solutions/2.1-Segwit-Version-1-Solutions.txt new file mode 100644 index 000000000..b086a9b6f --- /dev/null +++ b/solutions/2.1-Segwit-Version-1-Solutions.txt @@ -0,0 +1,97 @@ +#### 2.1.9 Generate segwit v1 addresses for a MuSig aggregate pubkey and a single-key +``` +# Generate individual key pairs +privkey1 = ECKey().generate() +privkey2 = ECKey().generate() +privkey3 = ECKey().generate() +pk1 = privkey1.get_pubkey() +pk2 = privkey2.get_pubkey() +pk3 = privkey3.get_pubkey() + +# Generate MuSig key +c_map, pk_musig = generate_musig_key([pk1, pk2]) + +# Multiply individual keys with challenges +privkey1_c = privkey1.mul(c_map[pk1]) +privkey2_c = privkey2.mul(c_map[pk2]) +pk1_c = pk1.mul(c_map[pk1]) +pk2_c = pk2.mul(c_map[pk2]) + +# Create a segwit v1 address for the MuSig aggregate pubkey +# Use segwit_addr.encode("bcrt", witness_version, witness_program) +pubkey_data_musig = pk_musig.get_bytes() +program_musig = bytes([pubkey_data_musig[0] & 1]) + pubkey_data_musig[1:] +version = 0x01 +address_musig = segwit_addr.encode("bcrt", version, program_musig) +print("2-of-2 musig: ", address_musig) + +# Create segwit v1 address (single) +pubkey_data_single = pk2.get_bytes() +program_single = bytes([pubkey_data_single[0] & 1]) + pubkey_data_single[1:] +version = 0x01 +address_single = segwit_addr.encode("bcrt", version, program_single) +print("Single key: ", address_single) +``` + +#### 2.1.11 Instantiate a CTransaction object and populate the version, locktime inputs and output +``` +# Construct transaction which spends the musig segwit v1 output +spending_tx = CTransaction() +spending_tx.nVersion = 1 +spending_tx.nLockTime = 0 +outpoint = COutPoint(tx_musig.sha256, output_index) +spending_tx_in = CTxIn(outpoint = outpoint) +spending_tx.vin = [spending_tx_in] + +# Generate new Bitcoin Core wallet address +dest_addr = test.nodes[0].getnewaddress(address_type="bech32") +scriptpubkey = bytes.fromhex(test.nodes[0].getaddressinfo(dest_addr)['scriptPubKey']) +print("Destination address: {}\n".format(dest_addr)) + +# Determine minimum fee required for mempool acceptance +min_fee = int(test.nodes[0].getmempoolinfo()['mempoolminfee'] * 100000000) + +# Complete output which returns funds to Bitcoin Core wallet +amount_sat = int(amount_btc * 100000000) +dest_output= CTxOut(nValue=amount_sat-min_fee, scriptPubKey=scriptpubkey) +spending_tx.vout = [dest_output] +print("Spending transaction:\n{}\n".format(spending_tx)) + +``` + +#### 2.1.12 Create a valid bip-schnorr signature for the MuSig aggregate pubkey +``` +# Create sighash for ANY|ALL +hash_types = [0,1,2,3,0x81,0x82,0x83] +sighash_musig = TaprootSignatureHash(spending_tx, [output], hash_types[4], input_index = 0, scriptpath = False) + +# Generate individual nonces for participants and an aggregate nonce point +# Remember to negate the individual nonces if necessary +nonce1 = generate_schnorr_nonce() +nonce2 = generate_schnorr_nonce() +R_agg, negated = aggregate_schnorr_nonces([nonce1.get_pubkey(), nonce2.get_pubkey()]) + +# Create an aggregate signature +sig1 = sign_musig(privkey1_c, nonce1, R_agg, pk_musig, sighash_musig) +sig2 = sign_musig(privkey2_c, nonce2, R_agg, pk_musig, sighash_musig) +sig_agg = aggregate_musig_signatures([sig1, sig2]) +sig_agg += hash_types[4].to_bytes(1, 'big') +print("Aggregate signature is {}".format(sig_agg.hex())) +``` + +#### 2.1.13 Construct the witness, add it to the transaction and verify mempool acceptance +``` +# Construct transaction witness +witness = CScriptWitness() +witness.stack.append(sig_agg) +witness_in = CTxInWitness() +witness_in.scriptWitness = witness +spending_tx.wit.vtxinwit.append(witness_in) + +# Serialize Schnorr transaction for broadcast +spending_tx_str = spending_tx.serialize().hex() + +# Test mempool acceptance +assert test.nodes[0].testmempoolaccept([spending_tx_str])[0]['allowed'] +print("Success!") +``` \ No newline at end of file