# xpubverify.py — sample session using BIP39 Test Vector 1 # # Mnemonic: abandon abandon abandon abandon abandon abandon # abandon abandon abandon abandon abandon about # Passphrase: (none) # Master fingerprint: 73C5DA0A # # The seed phrase is never entered into xpubverify.py. Only the watch-only # extended public key is used. # # Four keys are exported from that one wallet, each at the standard path for a # different address type, and the tool is run on each in turn. # # The second block is the one worth reading. It is a bare xpub, which does not # record its script type, so the tool refuses to guess and lists all four # candidate templates instead. Only the BIP44 template is this wallet's, because # that key was exported from m/44'/0'/0'. The other three are the same key read # under a policy the wallet does not use. They are printed on purpose, to show # what the key alone cannot tell you. # # Every address below is independently reproducible with Sparrow, Electrum, or # Ian Coleman's BIP39 tool (iancoleman.io/bip39). # ──────────────────────────────────────────────────────────────── $ python3 xpubverify.py \ zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs \ --receive-count 5 --change-count 3 ============================================================ xpubverify v1.0.0, watch-only address derivation ============================================================ Source: zpub (mainnet) Depth: 3 Parent fingerprint: 7ef32bdb Key fingerprint: fd13aac9 Child number: 0' (hardened) Dispatch: declared-policy The zpub prefix declares P2WPKH under SLIP-0132, so one template is listed, at that template's standard path for account 0, m/84'/0'/0'. The prefix is a declaration by whatever serialised the key, not proof of its derivation history. Native SegWit (P2WPKH), BIP84, m/84'/0'/0' (declared by the prefix, a hint not a proof) Receive addresses: #0 bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu #1 bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g #2 bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z #3 bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3 #4 bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n Change addresses: #0 bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el #1 bc1qggnasd834t54yulsep6fta8lpjekv4zj6gv5rf #2 bc1qn8alfh45rlsj44pcdt0f2cadtztgnz4gq3h3uf $ python3 xpubverify.py \ xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj \ --receive-count 5 --change-count 3 ============================================================ xpubverify v1.0.0, watch-only address derivation ============================================================ Source: xpub (mainnet) Depth: 3 Parent fingerprint: 155bca59 Key fingerprint: 6cc9f252 Child number: 0' (hardened) Dispatch: four-candidates Bare xpub at depth 3 with a hardened child-number: it does not record its script type, so four candidate templates are listed and no path is claimed for the key. Each template's path is the standard path for that template at the account index read from the key (account 0), a convention, not the key's origin. At most one template is the wallet's policy: resolve it before using an address. Native SegWit (P2WPKH), BIP84, m/84'/0'/0' Receive addresses: #0 bc1qmxrw6qdh5g3ztfcwm0et5l8mvws4eva24kmp8m #1 bc1qdtsnq885fjjj2agaza36cnl0ztg32wvxqg5x0c #2 bc1qmansqj24utny54uag2ped8censfwnszplhg27m #3 bc1quzaj3tlv9y40ljzmpd2alnpmzhdfraprhla2wg #4 bc1q4nr4ugzx54l7p4wysa7xw5s5ypldyndl2t9fq7 Change addresses: #0 bc1qht5nernlk6pyytfy0q935y492rhlg28jhggde9 #1 bc1qyqrp7k29mppkq8s9x2y0u0t0sl3kz2w4sle8q4 #2 bc1qmwf4e47geyl564j258jpd3q2wteyldscpvldaq Legacy (P2PKH), BIP44, m/44'/0'/0' Receive addresses: #0 1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA #1 1Ak8PffB2meyfYnbXZR9EGfLfFZVpzJvQP #2 1MNF5RSaabFwcbtJirJwKnDytsXXEsVsNb #3 1MVGa13XFvvpKGZdX389iU8b3qwtmAyrsJ #4 1Gka4JdwhLxRwXaC6oLNH4YuEogeeSwqW7 Change addresses: #0 1J3J6EvPrv8q6AC3VCjWV45Uf3nssNMRtH #1 13vKxXzHXXd8HquAYdpkJoi9ULVXUgfpS5 #2 1M21Wx1nGrHMPaz52N2En7c624nzL4MYTk Nested SegWit (P2SH-P2WPKH), BIP49, m/49'/0'/0' Receive addresses: #0 3HkzTaFbEMWeJPLyNCNhPyGfZsVLDwdD3G #1 3FYpNH4eWWmqqrvcbjWpvSJYybEaGmCwZi #2 3Qnpgq3UEaRRqXNmMBJZCDmVmCHQUmshaF #3 31qdR9dnshdqJTq52vb8jM1MwbCPnbqtL7 #4 3HWLPs6RbafmtZTKFjmncWLBpJ3TXmcqrK Change addresses: #0 36NJG8MkpBmKgVtA6L6W84EJYgakGMFKrF #1 3Dym98YyPWgkspSGhSFBYq4r1BfqZRMK3J #2 3FjPxhz4t2ARYRJ1DG9w2VNY7kNN2BG3WH Taproot (P2TR), BIP86, m/86'/0'/0' Receive addresses: #0 bc1plguuppjuw5uk2rpyjnnzvwsuvy5ctswns9fsvhrvn4qt04ns4nmscf9eqf #1 bc1p03dpkjmygsql7hzjdzjndkh25rfrnqmhc7skd9vl665ukydyv3hq79e4za #2 bc1pwtd9c04vlkkuhr92d74pkjmr4ven7dl49n0vfpj6hmj0j2v988fsu7dgx0 #3 bc1pdagtdxcxjsjagsftf4xxsg8xdmrddf007t05utjkzy3d65xatzwqv35zc4 #4 bc1pntwrzwfpk3af09rhdfrvz8qjgdl0fn9mk4fgwceuursnlrqadu6sych30j Change addresses: #0 bc1p0ru6ym5zwtpvwhp2u8wqm4tjvaavler670wyhrly33fh08ph2qlqmvgjpk #1 bc1pmpssmtjec5708kwupczxwjhqx66shy7qzwdv4t93nrzy6grvttvsaqatvh #2 bc1pmkvz9gs2uukv88ufah582sjplfwadpa0xp9zypq9rae3rcjgn3lqjp0dr9 $ python3 xpubverify.py \ ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP \ --receive-count 5 --change-count 3 ============================================================ xpubverify v1.0.0, watch-only address derivation ============================================================ Source: ypub (mainnet) Depth: 3 Parent fingerprint: 3d05ff75 Key fingerprint: 3a161284 Child number: 0' (hardened) Dispatch: declared-policy The ypub prefix declares P2SH-P2WPKH under SLIP-0132, so one template is listed, at that template's standard path for account 0, m/49'/0'/0'. The prefix is a declaration by whatever serialised the key, not proof of its derivation history. Nested SegWit (P2SH-P2WPKH), BIP49, m/49'/0'/0' (declared by the prefix, a hint not a proof) Receive addresses: #0 37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf #1 3LtMnn87fqUeHBUG414p9CWwnoV6E2pNKS #2 3B4cvWGR8X6Xs8nvTxVUoMJV77E4f7oaia #3 38CahkVftQneLonbWtfWxiiaT2fdnzsEAN #4 37mbeJptxfQC6SNNLJ9a8efCY4BwBh5Kak Change addresses: #0 34K56kSjgUCUSD8GTtuF7c9Zzwokbs6uZ7 #1 3516F2wmK51jVRrggEJsTUBNWMSLLjzvJ2 #2 3Grd7y95JEDTSh9uiVF5q7z2qGzmkP19CV $ python3 xpubverify.py --taproot \ xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ \ --receive-count 5 --change-count 3 ============================================================ xpubverify v1.0.0, watch-only address derivation ============================================================ Source: xpub (mainnet) Depth: 3 Parent fingerprint: 035270da Key fingerprint: a7bea80d Child number: 0' (hardened) Dispatch: operator-chosen --taproot selected the Taproot (P2TR) template for this run, at its standard path for account 0, m/86'/0'/0'. That is the operator's choice, not a claim about the key. Taproot (P2TR), BIP86, m/86'/0'/0' (operator-chosen, not a claim about the key) Receive addresses: #0 bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr #1 bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh #2 bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8 #3 bc1py0vryk8aqusz65yzuudypggvswzkcpwtau8q0sjm0stctwup0xlqkkxler #4 bc1pjpp8nwqvhkx6kdna6vpujdqglvz2304twfd308ve5ppyxpmcjufs7k6xyr Change addresses: #0 bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7 #1 bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj #2 bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5 $ python3 xpubverify.py --json --receive-count 3 --change-count 1 \ zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs { "schema_version": 2, "dispatch": "declared-policy", "dispatch_reason": "slip132", "version_name": "zpub", "network": "mainnet", "declared_address_type": "p2wpkh", "requested_address_type": null, "source_extended_key": "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", "depth": 3, "child_index": 2147483648, "child_hardened": true, "account_index": 0, "parent_fingerprint": "7ef32bdb", "key_fingerprint": "fd13aac9", "derivation_path": "m/84'/0'/0'", "templates": [ { "address_type": "p2wpkh", "label": "Native SegWit (P2WPKH)", "bip": "BIP84", "path": "m/84'/0'/0'", "selection": "declared", "receive": [ "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g", "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z" ], "change": [ "bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el" ], "descriptor_receive": "wpkh(xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V/0/*)#kj7aqcx6", "descriptor_change": "wpkh(xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V/1/*)#8xmuadkz" } ], "derivation_path_note": "The zpub prefix declares P2WPKH under SLIP-0132, so one template is listed, at that template's standard path for account 0, m/84'/0'/0'. The prefix is a declaration by whatever serialised the key, not proof of its derivation history." } # Cross-checks. The zpub, ypub and --taproot blocks list one template each, and # their addresses are the canonical BIP39 TV1 vectors for BIP84, BIP49 and # BIP86. In the four-candidate block, the BIP44 template is the canonical TV1 # BIP44 vector. The other three templates are that same key read under a policy # the wallet does not use, so they are valid derivations of a different address # set and match no TV1 vector. That is what the block is for. # # Verified against: Sparrow Wallet, Electrum 4.6.2, Ian Coleman BIP39 tool. # # This transcript is generated by running the tool, not written by hand: # tools/gen-xpubverify-proof.sh in the Bitcoin Witness source tree. # # xpubverify.py is the free public component of Bitcoin Witness. # No seed phrase required. No installation. No external dependencies. # Source: github.com/BitcoinWitness/xpubverify # Full product: bitcoinwitness.org