Repository: bitcoin/bitcoin
File: test/functional/wallet_musig.py (Python)

Code before:

from test_framework.descriptors import descsum_create
from test_framework.key import H_POINT
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
    assert_equal,
    assert_greater_than,
)

PRIVKEY_RE = re.compile(r"^tr\((.+?)/.+\)#.{8}$")
PUBKEY_RE = re.compile(r"^tr\((\[.+?\].+?)/.+\)#.{8}$")
ORIGIN_PATH_RE = re.compile(r"^\[\w{8}(/.*)\].*$")
MULTIPATH_TWO_RE = re.compile(r"<(\d+);(\d+)>")
MUSIG_RE = re.compile(r"musig\((.*?)\)")
PLACEHOLDER_RE = re.compile(r"\$\d")

class WalletMuSigTest(BitcoinTestFramework):
    WALLET_NUM = 0
    def set_test_params(self):
        self.num_nodes = 1

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()

    def do_test(self, comment, pattern, sighash_type=None, scriptpath=False, nosign_wallets=None, only_one_musig_wallet=False):
        self.log.info(f"Testing {comment}")
        has_internal = MULTIPATH_TWO_RE.search(pattern) is not None

        wallets = []
        keys = []

        pat = pattern.replace("$H", H_POINT)

        # Figure out how many wallets are needed and create them
        expected_pubnonces = 0
        expected_partial_sigs = 0
        for musig in MUSIG_RE.findall(pat):
            musig_partial_sigs = 0
            for placeholder in PLACEHOLDER_RE.findall(musig):
                wallet_index = int(placeholder[1:])
                if nosign_wallets is None or wallet_index not in nosign_wallets:
                    expected_pubnonces += 1
                else:
                    musig_partial_sigs = None
                if musig_partial_sigs is not None:
                    musig_partial_sigs += 1
                if wallet_index < len(wallets):
                    continue
                wallet_name = f"musig_{self.WALLET_NUM}"
                self.WALLET_NUM += 1
                self.nodes[0].createwallet(wallet_name)

Review comment (bug):
In c7c9dc2d8634bc79e76783be113f8d42cee63c6e "test: Add musig failure scenarios"

`Miniscript` pattern or `Multipath` pattern?

Code after:

from test_framework.descriptors import descsum_create
from test_framework.key import H_POINT
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
    assert_equal,
    assert_greater_than,
)

PRIVKEY_RE = re.compile(r"^tr\((.+?)/.+\)#.{8}$")
PUBKEY_RE = re.compile(r"^tr\((\[.+?\].+?)/.+\)#.{8}$")
ORIGIN_PATH_RE = re.compile(r"^\[\w{8}(/.*)\].*$")
MULTIPATH_TWO_RE = re.compile(r"<(\d+);(\d+)>")
MUSIG_RE = re.compile(r"musig\((.*?)\)")
PLACEHOLDER_RE = re.compile(r"\$\d")

class WalletMuSigTest(BitcoinTestFramework):
    wallet_num = 0
    def set_test_params(self):
        self.num_nodes = 1

    def skip_test_if_missing_module(self):
        self.skip_if_no_wallet()

    # Create wallets and extract keys
    def create_wallets_and_keys_from_pattern(self, pat):
        wallets = []
        keys = []

        for musig in MUSIG_RE.findall(pat):
            for placeholder in PLACEHOLDER_RE.findall(musig):
                wallet_index = int(placeholder[1:])
                if wallet_index < len(wallets):
                    continue

                wallet_name = f"musig_{self.wallet_num}"
                self.wallet_num += 1
                self.nodes[0].createwallet(wallet_name)
                wallet = self.nodes[0].get_wallet_rpc(wallet_name)
                wallets.append(wallet)

                for priv_desc in wallet.listdescriptors(True)["descriptors"]:
                    desc = priv_desc["desc"]
                    if not desc.startswith("tr("):
                        continue
                    privkey = PRIVKEY_RE.search(desc).group(1)
                    break
                for pub_desc in wallet.listdescriptors()["descriptors"]:
                    desc = pub_desc["desc"]
                    if not desc.startswith("tr("):
                        continue