Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python33import sys4import json5import textwrap7max_pubkeys = 09if len(sys.argv) < 2:10print(11"This script converts BIP MuSig2 test vectors in a given directory to a C file that can be used in the test framework."12)13print("Usage: %s <dir>" % sys.argv[0])14sys.exit(1)17def hexstr_to_intarray(str):18return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)])21def create_init(name):22return """23static const struct musig_%s_vector musig_%s_vector = {24""" % (25name,26name,27)30def init_array(key):31return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ")34def init_arrays(key):35s = textwrap.indent("{\n", 4 * " ")36s += textwrap.indent(37",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " "38)39s += textwrap.indent("\n},\n", 4 * " ")40return s43def init_indices(array):44return " %d, { %s }" % (45len(array),46", ".join(map(str, array) if len(array) > 0 else "0"),47)50def init_is_xonly(case):51if len(case["tweak_indices"]) > 0:52return ", ".join(map(lambda x: "1" if x else "0", case["is_xonly"]))53return "0"56def init_optional_expected(case):57return hexstr_to_intarray(case["expected"]) if "expected" in case else 060def init_cases(cases, f):61s = textwrap.indent("{\n", 4 * " ")62for (i, case) in enumerate(cases):63s += textwrap.indent("%s\n" % f(case), 8 * " ")64s += textwrap.indent("},\n", 4 * " ")65return s68def finish_init():69return "};\n"72s = (73"""/**74* Automatically generated by %s.75*76* The test vectors for the KeySort function are included in this file. They can77* be found in src/modules/extrakeys/tests_impl.h. */78"""79% sys.argv[0]80)83s += """84enum MUSIG_ERROR {85MUSIG_PUBKEY,86MUSIG_TWEAK,87MUSIG_PUBNONCE,88MUSIG_AGGNONCE,89MUSIG_SECNONCE,90MUSIG_SIG,91MUSIG_SIG_VERIFY,92MUSIG_OTHER93};94"""96# key agg vectors97with open(sys.argv[1] + "/key_agg_vectors.json", "r") as f:98data = json.load(f)100max_key_indices = max(101len(test_case["key_indices"]) for test_case in data["valid_test_cases"]102)103max_tweak_indices = max(104len(test_case["tweak_indices"]) for test_case in data["error_test_cases"]105)106num_pubkeys = len(data["pubkeys"])107max_pubkeys = max(num_pubkeys, max_pubkeys)108num_tweaks = len(data["tweaks"])109num_valid_cases = len(data["valid_test_cases"])110num_error_cases = len(data["error_test_cases"])112# Add structures for valid and error cases113s += (114"""115struct musig_key_agg_valid_test_case {116size_t key_indices_len;117size_t key_indices[%d];118unsigned char expected[32];119};120"""121% max_key_indices122)123s += """124struct musig_key_agg_error_test_case {125size_t key_indices_len;126size_t key_indices[%d];127size_t tweak_indices_len;128size_t tweak_indices[%d];129int is_xonly[%d];130enum MUSIG_ERROR error;131};132""" % (133max_key_indices,134max_tweak_indices,135max_tweak_indices,136)138# Add structure for entire vector139s += """140struct musig_key_agg_vector {141unsigned char pubkeys[%d][33];142unsigned char tweaks[%d][32];143struct musig_key_agg_valid_test_case valid_case[%d];144struct musig_key_agg_error_test_case error_case[%d];145};146""" % (147num_pubkeys,148num_tweaks,149num_valid_cases,150num_error_cases,151)153s += create_init("key_agg")154# Add pubkeys and tweaks to the vector155s += init_arrays("pubkeys")156s += init_arrays("tweaks")158# Add valid cases to the vector159s += init_cases(160data["valid_test_cases"],161lambda case: "{ %s, { %s }},"162% (init_indices(case["key_indices"]), hexstr_to_intarray(case["expected"])),163)165def comment_to_error(case):166comment = case["comment"]167if "public key" in comment.lower():168return "MUSIG_PUBKEY"169elif "tweak" in comment.lower():170return "MUSIG_TWEAK"171else:172sys.exit("Unknown error")174# Add error cases to the vector175s += init_cases(176data["error_test_cases"],177lambda case: "{ %s, %s, { %s }, %s },"178% (179init_indices(case["key_indices"]),180init_indices(case["tweak_indices"]),181init_is_xonly(case),182comment_to_error(case),183),184)186s += finish_init()188# nonce gen vectors189with open(sys.argv[1] + "/nonce_gen_vectors.json", "r") as f:190data = json.load(f)192# The MuSig2 implementation only allows messages of length 32193data["test_cases"] = list(194filter(lambda c: c["msg"] is None or len(c["msg"]) == 64, data["test_cases"])195)197num_tests = len(data["test_cases"])199s += """200struct musig_nonce_gen_test_case {201unsigned char rand_[32];202int has_sk;203unsigned char sk[32];204unsigned char pk[33];205int has_aggpk;206unsigned char aggpk[32];207int has_msg;208unsigned char msg[32];209int has_extra_in;210unsigned char extra_in[32];211unsigned char expected_secnonce[97];212unsigned char expected_pubnonce[66];213};214"""216s += (217"""218struct musig_nonce_gen_vector {219struct musig_nonce_gen_test_case test_case[%d];220};221"""222% num_tests223)225s += create_init("nonce_gen")227def init_array_maybe(array):228return "%d , { %s }" % (2290 if array is None else 1,230hexstr_to_intarray(array) if array is not None else 0,231)233s += init_cases(234data["test_cases"],235lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s }, { %s } },"236% (237hexstr_to_intarray(case["rand_"]),238init_array_maybe(case["sk"]),239hexstr_to_intarray(case["pk"]),240init_array_maybe(case["aggpk"]),241init_array_maybe(case["msg"]),242init_array_maybe(case["extra_in"]),243hexstr_to_intarray(case["expected_secnonce"]),244hexstr_to_intarray(case["expected_pubnonce"]),245),246)248s += finish_init()250# nonce agg vectors
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.