def check_if_seed_valid(self, seed_phrase) -> bool: try: bip39.mnemonic_to_seed((" ".join(seed_phrase)).strip()) except ValueError: return False return True
def add_hot_wallet_keys( self, mnemonic, passphrase, paths, file_password, wallet_manager, testnet, keys_range=[0, 1000], keys_purposes=[], ): super().add_hot_wallet_keys( mnemonic, passphrase, paths, file_password, wallet_manager, testnet, keys_range=keys_range, keys_purposes=keys_purposes, ) rpc = wallet_manager.rpc.wallet( os.path.join(wallet_manager.rpc_path + "_hotstorage", self.alias) ) seed = bip39.mnemonic_to_seed(mnemonic, passphrase) master_blinding_key = master_blinding_from_seed(seed) rpc.importmasterblindingkey(master_blinding_key.secret.hex()) self.set_blinding_key(master_blinding_key.wif())
def set_seed_phrase(self, seed_phrase, passphrase): # requires a valid seed phrase or error will be thrown self.seed_phrase = seed_phrase self.seed = bip39.mnemonic_to_seed( (" ".join(self.seed_phrase)).strip(), passphrase) self.root = bip32.HDKey.from_seed( self.seed, version=NETWORKS[self.current_network]["xprv"]) self.fingerprint = self.root.child(0).fingerprint self.bip48_xprv = self.root.derive(self.hardened_derivation) self.bip48_xpub = self.bip48_xprv.to_public() self.tx = None self.inp_amount = None self.fee = None self.spend = None self.destinationaddress = None self.dest_addr_cnt = None self.change = None self.controller = None self.buttons = None self.ins = None self.outs = None self.camera_loop_timer = None self.camera_data = None self.is_camera_data = False self.qr_total_frames = 0 self.qr_cur_frame_count = 0 self.qr_data = [] self.frame_display = [] self.percentage_complete = 0 self.scan_started_ind = 0 self.scan_display_working = 0
def main(): # generate 16 bytes of entropy and # convert to a mnemonic phrase (12 words) entropy = bytes([random.getrandbits(8) for i in range(16)]) mnemonic = bip39.mnemonic_from_bytes(entropy) # or just define hardcoded: mnemonic = "alien visual jealous source coral memory embark certain radar capable clip edit" print(mnemonic) # convert to seed, empty password seed = bip39.mnemonic_to_seed(mnemonic) # convert to the root key # you can define the version - x/y/zprv for desired network root = bip32.HDKey.from_seed(seed, version=NETWORKS["test"]["xprv"]) print(root.to_base58()) print("\nBIP-44 - legacy") # derive account according to bip44 bip44_xprv = root.derive("m/44h/1h/0h") print(bip44_xprv.to_base58()) # corresponding master public key: bip44_xpub = bip44_xprv.to_public() print(bip44_xpub.to_base58()) # first 5 receiving addresses for i in range(5): # .key member is a public key for HD public keys # and a private key for HD private keys pub = bip44_xpub.derive("m/0/%d" % i).key sc = script.p2pkh(pub) print(sc.address(NETWORKS["test"])) print("\nBIP-84 - native segwit") # derive account according to bip84 bip84_xprv = root.derive("m/84h/1h/0h") # you can also change version of the key to get zpub (vpub on testnet) bip84_xprv.version = NETWORKS["test"]["zprv"] print(bip84_xprv.to_base58()) # corresponding master public key: bip84_xpub = bip84_xprv.to_public() print(bip84_xpub.to_base58()) # first 5 receiving addresses for i in range(5): pub = bip84_xpub.derive("m/0/%d" % i).key sc = script.p2wpkh(pub) print(sc.address(NETWORKS["test"])) print("\nBIP-49 - nested segwit") # derive account according to bip49 bip49_xprv = root.derive("m/49h/1h/0h") # you can also change version of the key to get ypub (upub on testnet) bip49_xprv.version = NETWORKS["test"]["yprv"] print(bip49_xprv.to_base58()) # corresponding master public key: bip49_xpub = bip49_xprv.to_public() print(bip49_xpub.to_base58()) # first 5 receiving addresses for i in range(5): pub = bip49_xpub.derive("m/0/%d" % i).key # use p2sh(p2wpkh(pubkey)) to get nested segwit scriptpubkey sc = script.p2sh(script.p2wpkh(pub)) print(sc.address(NETWORKS["test"]))
def _generate_seed(self) -> bool: self.seed = None try: self.seed = bip39.mnemonic_to_seed(self.mnemonic, password=self.passphrase, wordlist=self._wordlist) return True except Exception as e: return False
def test_bip39(self): for [seed, exp_mnemonic, hex_seed, xprv] in VECTORS: act_mnemonic = mnemonic_from_bytes(unhexlify(seed)) act_xkey = HDKey.from_seed( mnemonic_to_seed(act_mnemonic, password="******")) self.assertEqual(act_mnemonic, exp_mnemonic) self.assertTrue(mnemonic_is_valid(act_mnemonic)) self.assertEqual( hexlify(mnemonic_to_bytes(act_mnemonic)).decode(), seed) self.assertEqual(act_xkey.to_base58(), xprv)
def test_alternate_wordlist(self): # Spanish mnemonics validated via https://iancoleman.io/bip39/#spanish mnemonics = [ ("título paso humano cañón enfado ropero hueco cromo blusa turno fideo glaciar verano baba gordo fila trance íntimo rotar gustar sombra revés laguna jardín", "bb0c5656117fd52d995dafca2d692974e74cb7c713c35871a0915d7bda6122694b2b67664113b198d2c1dd828195587c7dec8d6179f93d2157d6a11d8d0a949d" ), ("natural tóxico choque regreso norte tarta uña prisión bulto ángulo fervor nariz", "30affe746f3a81816739c2dacc3de426084482b729c7b592cee0ff2bdf73315943a5da8d8da4afd767f905d5ded4b0ab3a948d7eff9834fca5e8691a186fee20" ), ] from .data.bip39_es import WORDLIST as ES_WORDLIST for mnemonic, expected_seed in mnemonics: self.assertTrue(mnemonic_is_valid(mnemonic, wordlist=ES_WORDLIST)) self.assertEqual( hexlify(mnemonic_to_seed(mnemonic, wordlist=ES_WORDLIST)).decode(), expected_seed) self.assertEqual( mnemonic_from_bytes(mnemonic_to_bytes(mnemonic, wordlist=ES_WORDLIST), wordlist=ES_WORDLIST), mnemonic)
def main(): # get root key from the mnemonic mnemonic = "alien visual jealous source coral memory embark certain radar capable clip edit" seed = bip39.mnemonic_to_seed(mnemonic) root = bip32.HDKey.from_seed(seed, version=NETWORKS["test"]["xprv"]) # get bip84-xpub to import to Bitcoin Core: # we will use the form [fingerprint/derivation]xpub # to import to Bitcoin Core with descriptors # first let's get the root fingerprint # we can get it from any child of the root key fingerprint = root.child(0).fingerprint hardened_derivation = "m/84h/1h/0h" # derive account according to bip84 bip84_xprv = root.derive(hardened_derivation) # corresponding master public key: bip84_xpub = bip84_xprv.to_public() print("[%s%s]%s" % (hexlify(fingerprint).decode('utf-8'), hardened_derivation[1:], bip84_xpub.to_base58())) # parse psbt transaction b64_psbt = "cHNidP8BAHICAAAAAY3LB6teEH6qJHluFYG3AQe8n0HDUcUSEuw2WIJ1ECDUAAAAAAD/////AoDDyQEAAAAAF6kU882+nVMDKGj4rKzjDB6NjyJqSBCHaPMhCgAAAAAWABQUbW8/trQg4d3PKL8WLi2kUa1BqAAAAAAAAQEfAMLrCwAAAAAWABTR6Cr4flM2A0LMGjGiaZ+fhod37SIGAhHf737H1jCUjkJ1K5DqFkaY0keihxeWBQpm1kDtVZyxGLMX7IZUAACAAQAAgAAAAIAAAAAAAAAAAAAAIgIDPtTTi27VFw59jdmWDV8b1YciQzhYGO7m8zB9CvD0brcYsxfshlQAAIABAACAAAAAgAEAAAAAAAAAAA==" # first convert it to binary raw = a2b_base64(b64_psbt) # then parse tx = psbt.PSBT.parse(raw) # print how much we are spending and where total_in = 0 for inp in tx.inputs: total_in += inp.witness_utxo.value print("Inputs:", total_in, "satoshi") change_out = 0 # value that goes back to us send_outputs = [] for i, out in enumerate(tx.outputs): # check if it is a change or not: change = False # should be one or zero for single-key addresses for pub in out.bip32_derivations: # check if it is our key if out.bip32_derivations[pub].fingerprint == fingerprint: hdkey = root.derive(out.bip32_derivations[pub].derivation) mypub = hdkey.key.get_public_key() if mypub != pub: raise ValueError("Derivation path doesn't look right") # now check if provided scriptpubkey matches sc = script.p2wpkh(mypub) if sc == tx.tx.vout[i].script_pubkey: change = True continue if change: change_out += tx.tx.vout[i].value else: send_outputs.append(tx.tx.vout[i]) print("Spending", total_in - change_out, "satoshi") fee = total_in - change_out for out in send_outputs: fee -= out.value print(out.value, "to", out.script_pubkey.address(NETWORKS["test"])) print("Fee:", fee, "satoshi") # sign the transaction tx.sign_with(root) raw = tx.serialize() # convert to base64 b64_psbt = b2a_base64(raw) # somehow b2a ends with \n... if b64_psbt[-1:] == b"\n": b64_psbt = b64_psbt[:-1] # print print("\nSigned transaction:") print(b64_psbt.decode('utf-8'))
def add_hot_wallet_keys( self, mnemonic, passphrase, paths, file_password, wallet_manager, testnet, keys_range=[0, 1000], keys_purposes=[], ): if type(keys_range[0]) == str: keys_range[0] = int(keys_range[0]) if type(keys_range[1]) == str: keys_range[1] = int(keys_range[1]) seed = bip39.mnemonic_to_seed(mnemonic, passphrase) root = bip32.HDKey.from_seed(seed) network = networks.NETWORKS["test" if testnet else "main"] root.version = network["xprv"] xprv = root.to_base58() # Load the wallet if not loaded self._load_wallet(wallet_manager) rpc = wallet_manager.rpc.wallet( os.path.join(wallet_manager.rpc_path + "_hotstorage", self.alias)) if file_password: rpc.walletpassphrase(file_password, 60) rpc.importmulti( [{ "desc": AddChecksum("sh(wpkh({}{}/0/*))".format( xprv, path.rstrip("/").replace("m", ""))), "range": keys_range, "timestamp": "now", } for path in paths] + [{ "desc": AddChecksum("sh(wpkh({}{}/1/*))".format( xprv, path.rstrip("/").replace("m", ""))), "range": keys_range, "timestamp": "now", } for path in paths], {"rescan": False}, ) xpubs = [root.derive(path).to_public().to_base58() for path in paths] # root fingerprint is fingerprint field of the first child master_fpr = root.child(0).fingerprint.hex() keys = [] for i in range(len(paths)): try: path = paths[i] xpub = xpubs[i] # detect slip132 version for xpubs slip132_prefix = bip32.detect_version(path, default="xpub", network=network) xpub = "[{}{}]{}\n".format( master_fpr, path.replace("m", ""), convert_xpub_prefix(xpub, slip132_prefix), ) keys.append( Key.parse_xpub( xpub, keys_purposes[i] if len(keys_purposes) > i else "")) except Exception: # TODO: This should never occur, but just in case, # we must make sure to catch it properly so it # doesn't crash the app no matter what. raise Exception("Failed to parse this xpub:\n" + "\n".join(xpub)) self.add_keys(keys)
def main(): # mnemonic we use mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" seed = bip39.mnemonic_to_seed(mnemonic) root = bip32.HDKey.from_seed(seed) # A few transactions b64psbts = [ # p2wsh, 2 inputs "cHNidP8BALICAAAAAq1DhxRK+mUH4T6uUNob8bUaZ7MP+44MW4+Y9bOxpjhZAAAAAAD9////aWclWQ+45HKrI07r878E2UrAupT2paT4QurbmtNjYNQBAAAAAP3///8CQEIPAAAAAAAiACCpkDPDhmIzPlkJrjw9A71xjbIUWf3VUB7ooFJhTVm04tjSIQEAAAAAIgAgjQKFDauIXsV5u23LBdYgOwX1FwGGrLiQfWzBtFKZ7dIAAAAATwEENYfPBD5i336AAAACQStJhNVJul7vHKbo83VdmuAW2m0WaXLKDlFANn7dUNoCNbhLMdw4Knz7Q7o6exdL6UFhQegW9nJb0SUStbLEpawUAgjLdzAAAIABAACAAAAAgAIAAIBPAQQ1h88EnbHQAIAAAAI/2Nc7x7iMpJNapTe/OJTV4oifqzQcYY9KV2+PGRjCdQJoww1WnSNqfcxXGyux0q1PqfmzUqgJNqKJCpmqI9t47BQmu4PEMAAAgAEAAIAAAACAAgAAgE8BBDWHzwS6wUg5gAAAAh1Pvr3ZZ+GvcUwJl9OPz2cLXOnTAcBEC7zDtqIOt3IcA1aOofNgUZFu0baQw54SqOcGA7KAvTDOXygfKRilU2OqFHPF2gowAACAAQAAgAAAAIACAACAAAEBK4CWmAAAAAAAIgAgiYAxcG7dnrEiZ4VHFVHOo18XCalvhZYuMqBr9n7HESQBBWlSIQJOjQgMfX26XEf+trHIEk3rYkEX5Y2NfrFKQARPcd2X8iEDBWHUgq25PfHvE+hlcBryJG7wo2y8jKUSPY7sd85OOMchA2iVcuKLD+2p1pgcAjfZ5d7b/sFt5xQ/aAoC7V0Vn3WHU64iBgJOjQgMfX26XEf+trHIEk3rYkEX5Y2NfrFKQARPcd2X8hwmu4PEMAAAgAEAAIAAAACAAgAAgAAAAAABAAAAIgYDBWHUgq25PfHvE+hlcBryJG7wo2y8jKUSPY7sd85OOMccAgjLdzAAAIABAACAAAAAgAIAAIAAAAAAAQAAACIGA2iVcuKLD+2p1pgcAjfZ5d7b/sFt5xQ/aAoC7V0Vn3WHHHPF2gowAACAAQAAgAAAAIACAACAAAAAAAEAAAAAAQErgJaYAAAAAAAiACAzd60wM9EFnPHSNbsSJfyipL8myVLVP2/vwzotVUSNxQEFaVIhAiKCMRLlzIhLkRbLIUIMx5KYJM0v6LcjW/mS6K7eFGwiIQKDzUflU23LeecRgzDo5IBCEvaWGfHW7JkNxzXvuc7FdCEDC5DtLoa61/Kk/pdpu0F9e6nKoRJIB9v7Ni377rZefgFTriIGAiKCMRLlzIhLkRbLIUIMx5KYJM0v6LcjW/mS6K7eFGwiHAIIy3cwAACAAQAAgAAAAIACAACAAAAAAAAAAAAiBgKDzUflU23LeecRgzDo5IBCEvaWGfHW7JkNxzXvuc7FdBwmu4PEMAAAgAEAAIAAAACAAgAAgAAAAAAAAAAAIgYDC5DtLoa61/Kk/pdpu0F9e6nKoRJIB9v7Ni377rZefgEcc8XaCjAAAIABAACAAAAAgAIAAIAAAAAAAAAAAAABAWlSIQKtIdmtKKuZrH7f2R4iIU8RWVOrCdHVWBCS+0e9pZJy/iEDoH074LrWPIA10hyXtBCJDT06GdLkA6+z/PxoJqomPHYhA6GoQ/otQdk71nUpYZFfbkSKdBkkSj4CuPTPYrzGp6JrU64iAgKtIdmtKKuZrH7f2R4iIU8RWVOrCdHVWBCS+0e9pZJy/hwCCMt3MAAAgAEAAIAAAACAAgAAgAEAAAAAAAAAIgIDoH074LrWPIA10hyXtBCJDT06GdLkA6+z/PxoJqomPHYcc8XaCjAAAIABAACAAAAAgAIAAIABAAAAAAAAACICA6GoQ/otQdk71nUpYZFfbkSKdBkkSj4CuPTPYrzGp6JrHCa7g8QwAACAAQAAgAAAAIACAACAAQAAAAAAAAAAAA==", # p2sh-p2wsh "cHNidP8BAHICAAAAAR30J629i3Y/R8woRpLQ9JUa31rKxyM+Ny4NEsme48GWAAAAAAD9////Atw5XQUAAAAAF6kUdSESczdYagEyToVUSXyT8VTNz+OHgJaYAAAAAAAWABTmav7/w4OOcfCiewfjsA7eaujhYAAAAABPAQQ1h88EPmLffoAAAAHdXEj2dn8EYJ+rRdXEYu5laq6lJI5Mp+3t63ckwty05QKrJBNPewhwQaGPYRif6+XaxozFXvTXn7pU24H6fRy1FxQCCMt3MAAAgAEAAIAAAACAAQAAgE8BBDWHzwSdsdAAgAAAAeeOv56oeaaFTrNonMKDHk1C8brbWGFvdlecVue+v0/RAn/g4yI3oYsyen7OOcT7caYl4Mn7nQbyonHcusUR+GhzFCa7g8QwAACAAQAAgAAAAIABAACATwEENYfPBLrBSDmAAAABpzrb4oeEh2NNy/w/fr3osfyZTx7AaGDPAcP+LqeR3bYC5ioqmXPuazp69HwimlvecLylm9BLuyl/VpPXqiVrl20Uc8XaCjAAAIABAACAAAAAgAEAAIAAAQEgAOH1BQAAAAAXqRSv3gkn8731qcPbSDu4TJOlJJZ/PocBBCIAIOeiFBX5x0vX6CacrAUVovrs1DDCcKJS5qptFS3sjpDpAQVpUiECZ+pFYkOTVjB+eG+vQFA3MNjZWiA6DjRcs1Wl36A/zgMhA2Ygckuwjah29wiVRgA6wFx51+6ayrzeCIQ2eE4zfxPtIQOnUlBn22cn2CPCZkMSI6cDaZK2SlLV20rT6pqMoQCJsFOuIgYCZ+pFYkOTVjB+eG+vQFA3MNjZWiA6DjRcs1Wl36A/zgMcc8XaCjAAAIABAACAAAAAgAEAAIAAAAAAAAAAACIGA2Ygckuwjah29wiVRgA6wFx51+6ayrzeCIQ2eE4zfxPtHCa7g8QwAACAAQAAgAAAAIABAACAAAAAAAAAAAAiBgOnUlBn22cn2CPCZkMSI6cDaZK2SlLV20rT6pqMoQCJsBwCCMt3MAAAgAEAAIAAAACAAQAAgAAAAAAAAAAAAAEAIgAgZBwTq05RkpqKv6FV6LQjuM07Qv0/bYfWVc9NUQOFvwQBAWlSIQI32jVSdTgeu7+YZKrWfgOZ2J/LV36c5rBoApTzhrNlDCEC+hEqm3XmRt862AFFeyJ7p1m8A+V7czj6OajUNgCfg4EhA/MUVfxGh4k3Po3LB8CmMRsvIHcGNO0elUgETaITZA3UU64iAgI32jVSdTgeu7+YZKrWfgOZ2J/LV36c5rBoApTzhrNlDBwCCMt3MAAAgAEAAIAAAACAAQAAgAEAAAAAAAAAIgIC+hEqm3XmRt862AFFeyJ7p1m8A+V7czj6OajUNgCfg4EcJruDxDAAAIABAACAAAAAgAEAAIABAAAAAAAAACICA/MUVfxGh4k3Po3LB8CmMRsvIHcGNO0elUgETaITZA3UHHPF2gowAACAAQAAgAAAAIABAACAAQAAAAAAAAAAAA==", # p2wpkh "cHNidP8BAHECAAAAAc88WMMpgq4gUIjZvUnrmwKs3009rnalFsazBrFd46FOAAAAAAD9////Anw/XQUAAAAAFgAULzSqHPAKU7BVopGgOn1F8KaYi1KAlpgAAAAAABYAFOZq/v/Dg45x8KJ7B+OwDt5q6OFgAAAAAAABAR8A4fUFAAAAABYAFNDEo+8J6Ze26Z45flGP4+QaEYyhIgYC56slN7XUnpcDCargbp5J82zhyf671E7I4NHMoLT5wxkYc8XaClQAAIABAACAAAAAgAAAAAAAAAAAACICA11J7M1U0AmeQ2did8em1GJdYR2oil30m/lReneRp3elGHPF2gpUAACAAQAAgAAAAIABAAAAAAAAAAAA", # p2sh-p2wpkh "cHNidP8BAHICAAAAAXbva/K90EDzwdg6zLl0OfGrsaVWrR0PUpaB/6foypSKAQAAAAD9////Apw9XQUAAAAAF6kUJR3RFFeiWcO6R+XMo3F/5CFOApiHgJaYAAAAAAAWABTmav7/w4OOcfCiewfjsA7eaujhYAAAAAAAAQEgAOH1BQAAAAAXqRQzbKoT4IuWCAoytdgY1ZtKs7NnQocBBBYAFDiXH3OTD2wUHZd6xP1KcnyFSTWzIgYDoa+ASsEIqKUXghmMLQNLKL+QyIA/WlP3Ynb6aaTq538Yc8XaCjEAAIABAACAAAAAgAAAAAAAAAAAAAEAFgAUcL6x4EpQCUDp86uqZuGkmsVbjzUiAgKi/ImWxSYiSLXa78Wk0M3NAMEwR9DLEwKBNupjDYdahxhzxdoKMQAAgAEAAIAAAACAAQAAAAAAAAAAAA==", ] for i, b64psbt in enumerate(b64psbts): print("\nTransaction #%d" % (i + 1)) raw = a2b_base64(b64psbt) tx = psbt.PSBT.parse(raw) # Check inputs of the transaction and check that they use the same script type # For multisig parsed policy will look like this: # { script_type: p2wsh, cosigners: [xpubs strings], m: 2, n: 3} policy = None inp_amount = 0 for inp in tx.inputs: inp_amount += inp.witness_utxo.value # get policy of the input inp_policy = get_policy(inp, inp.witness_utxo.script_pubkey, tx.xpubs) # if policy is None - assign current if policy is None: policy = inp_policy # otherwise check that everything in the policy is the same else: # check policy is the same if policy != inp_policy: raise RuntimeError("Mixed inputs in the transaction") wallet = "Native Segwit " if "p2sh-" in policy["type"]: wallet = "Nested Segwit " if "m" in policy: wallet += "Multisig (%d of %d)" % (policy["m"], policy["n"]) else: wallet += "Single sig" print("Spending from: %s" % wallet) print("Input amount: %d sat" % inp_amount) # now go through outputs and check if they are change spending = 0 change = 0 for i, out in enumerate(tx.outputs): out_policy = get_policy(out, tx.tx.vout[i].script_pubkey, tx.xpubs) is_change = False # if policy is the same - probably change if out_policy == policy: # double-check that it's change # we already checked in get_cosigners and parse_multisig # that pubkeys are generated from cosigners, # and witness script is corresponding multisig # so we only need to check that scriptpubkey is generated from # witness script # empty script by default sc = script.Script(b"") # multisig, we know witness script if policy["type"] == "p2wsh": sc = script.p2wsh(out.witness_script) elif policy["type"] == "p2sh-p2wsh": sc = script.p2sh(script.p2wsh(out.witness_script)) # single-sig elif "pkh" in policy["type"]: if len(out.bip32_derivations.values()) > 0: der = list( out.bip32_derivations.values())[0].derivation my_pubkey = root.derive(der) if policy["type"] == "p2wpkh": sc = script.p2wpkh(my_pubkey) elif policy["type"] == "p2sh-p2wpkh": sc = script.p2sh(script.p2wpkh(my_pubkey)) if sc.data == tx.tx.vout[i].script_pubkey.data: is_change = True if is_change: change += tx.tx.vout[i].value print("Change %d sats" % tx.tx.vout[i].value) else: spending += tx.tx.vout[i].value print("Spending %d sats to %s" % (tx.tx.vout[i].value, tx.tx.vout[i].script_pubkey.address())) fee = inp_amount - change - spending print("Fee: %d sats" % fee)
from unittest import TestCase from embit.psbtview import PSBTView from embit.psbt import PSBT, InputScope from embit import bip32, bip39 from binascii import a2b_base64, b2a_base64 from io import BytesIO ROOT = bip32.HDKey.from_seed( bip39.mnemonic_to_seed( "toy fault beef holiday later unit boring merge shield detail scrap negative" )) # tprv8ZgxMBicQKsPeDhmZay7WoN2W9gkmZNv4bkPRgCsaqKAnafo2YkpmJFQUAv34PTdYciNteTu8A1tvDUBsusThseGfiPkdFAniazFzxRd8xv PSBTS = [ # native segwit single key, 3 inputs 4 outputs, version is None "cHNidP8BAP0NAQIAAAADzs+bdp5MDPJOynM/GVqxv0TxNfN65lYTcGjA8cD2NQABAAAAAP3////1a2FpOvW/FfVn5ct5op0HWQPcF4A4CiD1PuhzM0hvrQAAAAAA/f///2MhMb9J3pwquctpXFlr8LsbwD92XoSKkuwvoNt5prEyAQAAAAD9////BJ5YYgIAAAAAFgAUVE9NQrxzbpzaj8DvSA4+Vfb3S8+Aw8kBAAAAACIAILUTol87mgHQEKeZJcuNUJjXFfUupwaSGuS7QTdBQ1TW5pWYAAAAAAAWABSyijJ33ycsST0veZC7xCcjMLvGnoCWmAAAAAAAFgAUfwP8SJ6OrkWYgi79Rmtboh4vb6AAAAAAAAEAcQIAAAABuN9N7UQtW4/kL7p6GZ+7mJBDtdrC/DLvjimTOlG61vwAAAAAAP7///8CZnl3IwEAAAAWABTW7Yti5UnKO3zm7e0HWxgroI9k8YCWmAAAAAAAFgAUCCFopyBBzsmGQ+okmceR7NCUMRkAAAAAAQEfgJaYAAAAAAAWABQIIWinIEHOyYZD6iSZx5Hs0JQxGSIGAqmBsz9+uSoKzzc3UJKj73Z/nJbAJO8qGivBThNWuQtcGCYUvcRUAACAAQAAgAAAAIAAAAAAAgAAAAABAH0CAAAAAWMhMb9J3pwquctpXFlr8LsbwD92XoSKkuwvoNt5prEyAAAAAAD9////AoCWmAAAAAAAFgAUmt3MvhOm8evmqSbslu0VMw4ajTRcLDEBAAAAACIAIAzATi9Gij9I+WDMngJmvUJUfdBpvUc5SHXeB2kErkrHAAAAAAEBH4CWmAAAAAAAFgAUmt3MvhOm8evmqSbslu0VMw4ajTQiBgOBjOkF2dUPQVfZDgvAq1AJnIyXnxIndmAv9CatqeHP9hgmFL3EVAAAgAEAAIAAAACAAAAAAAEAAAAAAQB9AgAAAAG4303tRC1bj+QvunoZn7uYkEO12sL8Mu+OKZM6UbrW/AEAAAAA/f///wKAw8kBAAAAACIAIFwqEzbsUSf+/PC7SPfqGuy75tAydrgFkmv8DAEW6eNi5hwsBAAAAAAWABT9hflT6IkrLKaHS+x3zqee7ExP/AAAAAABAR/mHCwEAAAAABYAFP2F+VPoiSsspodL7HfOp57sTE/8IgYC420o3I+pkBOSVZyER/NTtZTLtP1iIKPdZK99ilI9UDoYJhS9xFQAAIABAACAAAAAgAEAAAAAAAAAAAAAIgICfM8IJ5ATUtpeuQOLpQbXkqvhs5mriKEpWLHod5vWAo4YJhS9xFQAAIABAACAAAAAgAEAAAACAAAAACICAujQqWO9vOEDwyUZqbeseGADN99ME/YWtJJTjW9/ag25GCYUvcRUAACAAQAAgAAAAIAAAAAAAwAAAAA=", # same transaction, version = 2 "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQMBBQEEAfsEAgAAAAABAHECAAAAAbjfTe1ELVuP5C+6ehmfu5iQQ7Xawvwy744pkzpRutb8AAAAAAD+////AmZ5dyMBAAAAFgAU1u2LYuVJyjt85u3tB1sYK6CPZPGAlpgAAAAAABYAFAghaKcgQc7JhkPqJJnHkezQlDEZAAAAAAEBH4CWmAAAAAAAFgAUCCFopyBBzsmGQ+okmceR7NCUMRkiBgKpgbM/frkqCs83N1CSo+92f5yWwCTvKhorwU4TVrkLXBgmFL3EVAAAgAEAAIAAAACAAAAAAAIAAAABDiDOz5t2nkwM8k7Kcz8ZWrG/RPE183rmVhNwaMDxwPY1AAEPBAEAAAABEAT9////AAEAfQIAAAABYyExv0nenCq5y2lcWWvwuxvAP3ZehIqS7C+g23mmsTIAAAAAAP3///8CgJaYAAAAAAAWABSa3cy+E6bx6+apJuyW7RUzDhqNNFwsMQEAAAAAIgAgDMBOL0aKP0j5YMyeAma9QlR90Gm9RzlIdd4HaQSuSscAAAAAAQEfgJaYAAAAAAAWABSa3cy+E6bx6+apJuyW7RUzDhqNNCIGA4GM6QXZ1Q9BV9kOC8CrUAmcjJefEid2YC/0Jq2p4c/2GCYUvcRUAACAAQAAgAAAAIAAAAAAAQAAAAEOIPVrYWk69b8V9Wfly3minQdZA9wXgDgKIPU+6HMzSG+tAQ8EAAAAAAEQBP3///8AAQB9AgAAAAG4303tRC1bj+QvunoZn7uYkEO12sL8Mu+OKZM6UbrW/AEAAAAA/f///wKAw8kBAAAAACIAIFwqEzbsUSf+/PC7SPfqGuy75tAydrgFkmv8DAEW6eNi5hwsBAAAAAAWABT9hflT6IkrLKaHS+x3zqee7ExP/AAAAAABAR/mHCwEAAAAABYAFP2F+VPoiSsspodL7HfOp57sTE/8IgYC420o3I+pkBOSVZyER/NTtZTLtP1iIKPdZK99ilI9UDoYJhS9xFQAAIABAACAAAAAgAEAAAAAAAAAAQ4gYyExv0nenCq5y2lcWWvwuxvAP3ZehIqS7C+g23mmsTIBDwQBAAAAARAE/f///wABAwieWGICAAAAAAEEFgAUVE9NQrxzbpzaj8DvSA4+Vfb3S88AAQMIgMPJAQAAAAABBCIAILUTol87mgHQEKeZJcuNUJjXFfUupwaSGuS7QTdBQ1TWACICAnzPCCeQE1LaXrkDi6UG15Kr4bOZq4ihKVix6Heb1gKOGCYUvcRUAACAAQAAgAAAAIABAAAAAgAAAAEDCOaVmAAAAAAAAQQWABSyijJ33ycsST0veZC7xCcjMLvGngAiAgLo0KljvbzhA8MlGam3rHhgAzffTBP2FrSSU41vf2oNuRgmFL3EVAAAgAEAAIAAAACAAAAAAAMAAAABAwiAlpgAAAAAAAEEFgAUfwP8SJ6OrkWYgi79Rmtboh4vb6AA", # nested segwit, 3 inp 3 outs "cHNidP8BAOMCAAAAAxTKkAuekd781k0dtVGHQB6rfsnXbOoRI8nCZaODOTuKAQAAAAD9////GzrJPRcrKOpTM/UuJtU9BHoPOauuwOwTbze8A98e9jkAAAAAAP3///8zUYet5YsLr1JURBtw6y1J+aOIFUYppSUF98NMQ0SGqAEAAAAA/f///wPA4eQAAAAAABYAFDi4Qvb/any4sD/j++An4QeQyZ8ZABu3AAAAAAAWABSZhIQZ8nU642HEO2Ad58HNlLuNX0LFLQAAAAAAF6kUmyPIidm0uYu4R4cd4G/ePLv/KL6HAAAAAAABAHICAAAAAc7Pm3aeTAzyTspzPxlasb9E8TXzeuZWE3BowPHA9jUAAAAAAAD+////Alji3iIBAAAAFgAUhtwMeBRjJ59uRi16BF//sCuKaQ+AlpgAAAAAABepFF/+tUQewdyL8GiqX8J+muPiFo88hwAAAAABASCAlpgAAAAAABepFF/+tUQewdyL8GiqX8J+muPiFo88hwEEFgAUlGEc5+39qmo7NT7GrA6unMA28sciBgObyQ41qMrOxju4XxzMINIB1ZGui3QjZEVfIZ7WGJnS2xgmFL3EMQAAgAEAAIAAAACAAAAAAAAAAAAAAQByAgAAAAGhyWgevahbYnWS3rmRhpZign4sGaq4mOdM4D8UKs2q1AAAAAAA/v///wKAlpgAAAAAABepFPAAkmx/dRJAKf+Lw49LAzeIh975h/JabSkBAAAAFgAUfWxZ1njvA0Yr7QVYEme4CDHh7qMAAAAAAQEggJaYAAAAAAAXqRTwAJJsf3USQCn/i8OPSwM3iIfe+YcBBBYAFM6hs2GlOxZSDUae67LI3aiHb3G/IgYCtyN18tSmyoFClQl6Fa87TtEnrMZH6NJoRooWChmuHd0YJhS9xDEAAIABAACAAAAAgAAAAAABAAAAAAEAcgIAAAABV2MIaeVqMWANJDD2CUpsLwzV+Kbyg5cpQYob80WZZSMAAAAAAP7///8C8lptKQEAAAAWABTXvwQx1pNX7/TEGt2BYOIovqhL4ICWmAAAAAAAF6kU9x5iJ+lOE1CyDvzfZ+6pFjHKnviHAAAAAAEBIICWmAAAAAAAF6kU9x5iJ+lOE1CyDvzfZ+6pFjHKnviHAQQWABQ5QbIFFdKLyxFSNm1hZ8S/28cjGSIGAz0meCzo95/qUlBrj9hp+T8hPjVPMIsYK7uTj4iq+Wl6GCYUvcQxAACAAQAAgAAAAIAAAAAAAgAAAAAAAAEAFgAUvQVfGjGkvg8XROgHZhlqhzm/ZOkiAgJXWxh/KFZ6O5bcwrw6M8BwIWtYoHYbrU7JqNOgbTtwxBgmFL3EMQAAgAEAAIAAAACAAQAAAAAAAAAA", # same psbt, version = 2 "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQMBBQEDAfsEAgAAAAABAHICAAAAAc7Pm3aeTAzyTspzPxlasb9E8TXzeuZWE3BowPHA9jUAAAAAAAD+////Alji3iIBAAAAFgAUhtwMeBRjJ59uRi16BF//sCuKaQ+AlpgAAAAAABepFF/+tUQewdyL8GiqX8J+muPiFo88hwAAAAABASCAlpgAAAAAABepFF/+tUQewdyL8GiqX8J+muPiFo88hwEEFgAUlGEc5+39qmo7NT7GrA6unMA28sciBgObyQ41qMrOxju4XxzMINIB1ZGui3QjZEVfIZ7WGJnS2xgmFL3EMQAAgAEAAIAAAACAAAAAAAAAAAABDiAUypALnpHe/NZNHbVRh0Aeq37J12zqESPJwmWjgzk7igEPBAEAAAABEAT9////AAEAcgIAAAABocloHr2oW2J1kt65kYaWYoJ+LBmquJjnTOA/FCrNqtQAAAAAAP7///8CgJaYAAAAAAAXqRTwAJJsf3USQCn/i8OPSwM3iIfe+YfyWm0pAQAAABYAFH1sWdZ47wNGK+0FWBJnuAgx4e6jAAAAAAEBIICWmAAAAAAAF6kU8ACSbH91EkAp/4vDj0sDN4iH3vmHAQQWABTOobNhpTsWUg1GnuuyyN2oh29xvyIGArcjdfLUpsqBQpUJehWvO07RJ6zGR+jSaEaKFgoZrh3dGCYUvcQxAACAAQAAgAAAAIAAAAAAAQAAAAEOIBs6yT0XKyjqUzP1LibVPQR6DzmrrsDsE283vAPfHvY5AQ8EAAAAAAEQBP3///8AAQByAgAAAAFXYwhp5WoxYA0kMPYJSmwvDNX4pvKDlylBihvzRZllIwAAAAAA/v///wLyWm0pAQAAABYAFNe/BDHWk1fv9MQa3YFg4ii+qEvggJaYAAAAAAAXqRT3HmIn6U4TULIO/N9n7qkWMcqe+IcAAAAAAQEggJaYAAAAAAAXqRT3HmIn6U4TULIO/N9n7qkWMcqe+IcBBBYAFDlBsgUV0ovLEVI2bWFnxL/bxyMZIgYDPSZ4LOj3n+pSUGuP2Gn5PyE+NU8wixgru5OPiKr5aXoYJhS9xDEAAIABAACAAAAAgAAAAAACAAAAAQ4gM1GHreWLC69SVEQbcOstSfmjiBVGKaUlBffDTENEhqgBDwQBAAAAARAE/f///wABAwjA4eQAAAAAAAEEFgAUOLhC9v9qfLiwP+P74CfhB5DJnxkAAQMIABu3AAAAAAABBBYAFJmEhBnydTrjYcQ7YB3nwc2Uu41fAAEAFgAUvQVfGjGkvg8XROgHZhlqhzm/ZOkiAgJXWxh/KFZ6O5bcwrw6M8BwIWtYoHYbrU7JqNOgbTtwxBgmFL3EMQAAgAEAAIAAAACAAQAAAAAAAAABAwhCxS0AAAAAAAEEF6kUmyPIidm0uYu4R4cd4G/ePLv/KL6HAA==", # legacy "cHNidP8BAMMCAAAAA/8CS6+D7pDNQd/uwrrbG8k78U9QFdzV559ZhQ1EVYGEAAAAAAD+////70WX911pnoWryXdRJlFlGJyuuh5NKwXkaz76h/meNogAAAAAAP7///+O9uy981BmAPPPKXis8gBJNmx+Q0cVhuVe2RaQJdI1BwAAAAAA/v///wJAeH0BAAAAABYAFPSXFN4nngE2GMROpImI5XRxRkCXP0lMAAAAAAAWABTpOJ/mb2jdP898bWAmZuD7Njn/awAAAAAAAQB0AgAAAAGCo1OTQLigQvL2iP3OK/kA4+1Mwv2Y4ErI0Zl4E5j3SQAAAAAA/v///wKAlpgAAAAAABl2qRQhggw2j7lZq5FRG/W8Wxtq9pwcbYis/eXoSQAAAAAWABT0gC7h3PBRXI9jFN868mhubmC+2gAAAAAiBgNoW2ak4dJxu2tMB1xKJ8i0IZ1upTKHilgX2NDjWlH58gwmFL3EAAAAAAEAAAAAAQB0AgAAAAGnjGh1Fuk1Y5dz4QE9r+3oEUfuAiK9MGm3tjnPkXvShgAAAAAA/v///wKAlpgAAAAAABl2qRSz5j68l/pmSGDdCy8X1hM5LcUaxYisFOboSQAAAAAWABTh9XHu7fEdxJ2bqU/DGbrfvU42UAAAAAAiBgNYvS1u+b8fq7DcmL63qHttHinxbZLp9rhbOnmyBUCeTwwmFL3EAAAAAAIAAAAAAQB0AgAAAAFgB75i+OT1O7SqGBsTmTajlxpDsnLc/iwxwfbvr6jLVwAAAAAA/v///wKAlpgAAAAAABl2qRQBEqxvaLOlc1lLiadG5yQ5bilaRYisxf7yQwAAAAAWABTCShfxLkJyJ3/wjVLDRtChyK5v1QAAAAAiBgMmekh8kmFBB8c2g+PQLCgSuginKK+UJuxobtdRZ4FAiAwmFL3EAAAAAAAAAAAAACICAvjQsARdPkCfLoeR/ysMrMfEKmvRZ7guQexDh5x6Tpj7EGMusisAAACAAQAAgAEAAIAA", # same, psbt version 2 "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQMBBQECAfsEAgAAAAABAHQCAAAAAYKjU5NAuKBC8vaI/c4r+QDj7UzC/ZjgSsjRmXgTmPdJAAAAAAD+////AoCWmAAAAAAAGXapFCGCDDaPuVmrkVEb9bxbG2r2nBxtiKz95ehJAAAAABYAFPSALuHc8FFcj2MU3zryaG5uYL7aAAAAACIGA2hbZqTh0nG7a0wHXEonyLQhnW6lMoeKWBfY0ONaUfnyDCYUvcQAAAAAAQAAAAEOIP8CS6+D7pDNQd/uwrrbG8k78U9QFdzV559ZhQ1EVYGEAQ8EAAAAAAEQBP7///8AAQB0AgAAAAGnjGh1Fuk1Y5dz4QE9r+3oEUfuAiK9MGm3tjnPkXvShgAAAAAA/v///wKAlpgAAAAAABl2qRSz5j68l/pmSGDdCy8X1hM5LcUaxYisFOboSQAAAAAWABTh9XHu7fEdxJ2bqU/DGbrfvU42UAAAAAAiBgNYvS1u+b8fq7DcmL63qHttHinxbZLp9rhbOnmyBUCeTwwmFL3EAAAAAAIAAAABDiDvRZf3XWmehavJd1EmUWUYnK66Hk0rBeRrPvqH+Z42iAEPBAAAAAABEAT+////AAEAdAIAAAABYAe+Yvjk9Tu0qhgbE5k2o5caQ7Jy3P4sMcH276+oy1cAAAAAAP7///8CgJaYAAAAAAAZdqkUARKsb2izpXNZS4mnRuckOW4pWkWIrMX+8kMAAAAAFgAUwkoX8S5Ccid/8I1Sw0bQociub9UAAAAAIgYDJnpIfJJhQQfHNoPj0CwoEroIpyivlCbsaG7XUWeBQIgMJhS9xAAAAAAAAAAAAQ4gjvbsvfNQZgDzzyl4rPIASTZsfkNHFYblXtkWkCXSNQcBDwQAAAAAARAE/v///wABAwhAeH0BAAAAAAEEFgAU9JcU3ieeATYYxE6kiYjldHFGQJcAIgIC+NCwBF0+QJ8uh5H/Kwysx8Qqa9FnuC5B7EOHnHpOmPsQYy6yKwAAAIABAACAAQAAgAEDCD9JTAAAAAAAAQQWABTpOJ/mb2jdP898bWAmZuD7Njn/awA=", # 1-of-2 multisig "cHNidP8BAMMCAAAAA+9Fl/ddaZ6Fq8l3USZRZRicrroeTSsF5Gs++of5njaIAAAAAAD+/////wJLr4PukM1B3+7CutsbyTvxT1AV3NXnn1mFDURVgYQAAAAAAP7///+O9uy981BmAPPPKXis8gBJNmx+Q0cVhuVe2RaQJdI1BwAAAAAA/v///wI/SUwAAAAAABYAFCcLXGMkxtEHdt09w5nGGub5dJ8JQHh9AQAAAAAWABT0lxTeJ54BNhjETqSJiOV0cUZAlwAAAAAAAQB0AgAAAAGnjGh1Fuk1Y5dz4QE9r+3oEUfuAiK9MGm3tjnPkXvShgAAAAAA/v///wKAlpgAAAAAABl2qRSz5j68l/pmSGDdCy8X1hM5LcUaxYisFOboSQAAAAAWABTh9XHu7fEdxJ2bqU/DGbrfvU42UAAAAAAiBgNYvS1u+b8fq7DcmL63qHttHinxbZLp9rhbOnmyBUCeTwwmFL3EAAAAAAIAAAAAAQB0AgAAAAGCo1OTQLigQvL2iP3OK/kA4+1Mwv2Y4ErI0Zl4E5j3SQAAAAAA/v///wKAlpgAAAAAABl2qRQhggw2j7lZq5FRG/W8Wxtq9pwcbYis/eXoSQAAAAAWABT0gC7h3PBRXI9jFN868mhubmC+2gAAAAAiBgNoW2ak4dJxu2tMB1xKJ8i0IZ1upTKHilgX2NDjWlH58gwmFL3EAAAAAAEAAAAAAQB0AgAAAAFgB75i+OT1O7SqGBsTmTajlxpDsnLc/iwxwfbvr6jLVwAAAAAA/v///wKAlpgAAAAAABl2qRQBEqxvaLOlc1lLiadG5yQ5bilaRYisxf7yQwAAAAAWABTCShfxLkJyJ3/wjVLDRtChyK5v1QAAAAAiBgMmekh8kmFBB8c2g+PQLCgSuginKK+UJuxobtdRZ4FAiAwmFL3EAAAAAAAAAAAAIgICv+hNVXqScnRDcCJsJcq35scU0SsSp08WNnVklPIs/DkQYy6yKwAAAIABAACAAwAAgAAA", # same, version 2 "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQMBBQECAfsEAgAAAAABAHQCAAAAAaeMaHUW6TVjl3PhAT2v7egRR+4CIr0wabe2Oc+Re9KGAAAAAAD+////AoCWmAAAAAAAGXapFLPmPryX+mZIYN0LLxfWEzktxRrFiKwU5uhJAAAAABYAFOH1ce7t8R3EnZupT8MZut+9TjZQAAAAACIGA1i9LW75vx+rsNyYvreoe20eKfFtkun2uFs6ebIFQJ5PDCYUvcQAAAAAAgAAAAEOIO9Fl/ddaZ6Fq8l3USZRZRicrroeTSsF5Gs++of5njaIAQ8EAAAAAAEQBP7///8AAQB0AgAAAAGCo1OTQLigQvL2iP3OK/kA4+1Mwv2Y4ErI0Zl4E5j3SQAAAAAA/v///wKAlpgAAAAAABl2qRQhggw2j7lZq5FRG/W8Wxtq9pwcbYis/eXoSQAAAAAWABT0gC7h3PBRXI9jFN868mhubmC+2gAAAAAiBgNoW2ak4dJxu2tMB1xKJ8i0IZ1upTKHilgX2NDjWlH58gwmFL3EAAAAAAEAAAABDiD/Akuvg+6QzUHf7sK62xvJO/FPUBXc1eefWYUNRFWBhAEPBAAAAAABEAT+////AAEAdAIAAAABYAe+Yvjk9Tu0qhgbE5k2o5caQ7Jy3P4sMcH276+oy1cAAAAAAP7///8CgJaYAAAAAAAZdqkUARKsb2izpXNZS4mnRuckOW4pWkWIrMX+8kMAAAAAFgAUwkoX8S5Ccid/8I1Sw0bQociub9UAAAAAIgYDJnpIfJJhQQfHNoPj0CwoEroIpyivlCbsaG7XUWeBQIgMJhS9xAAAAAAAAAAAAQ4gjvbsvfNQZgDzzyl4rPIASTZsfkNHFYblXtkWkCXSNQcBDwQAAAAAARAE/v///wAiAgK/6E1VepJydENwImwlyrfmxxTRKxKnTxY2dWSU8iz8ORBjLrIrAAAAgAEAAIADAACAAQMIP0lMAAAAAAABBBYAFCcLXGMkxtEHdt09w5nGGub5dJ8JAAEDCEB4fQEAAAAAAQQWABT0lxTeJ54BNhjETqSJiOV0cUZAlwA=", ]