def add(self, txs): """ adds transactions to the list without any rpc calls. assuming tx is already watched by the wallet. Transactions should be a dict with dicts with fields: "<txid>": { "txid", - hex txid "hex", - hex raw transaction "blockheight", - int blockheight if confirmed, None otherwise "blockhash", - str blockhash if confirmed, None otherwise "time", - int unix timestamp in seconds when tx was received "blocktime", - int unix timestamp in seconds the block was mined "conflicts", - list of txids spending the same inputs (rbf) "bip125-replaceable", - str ("yes" or "no") - is rbf enabled for this tx } """ # here we store all addresses in transactions # to set them used later addresses = [] # first we add all transactions to cache for txid in txs: tx = txs[txid] # find minimal from 3 times: maxtime = 10445238000 # TODO: change after 31 dec 2300 lol time = min( tx.get("blocktime", maxtime), tx.get("timereceived", maxtime), tx.get("time", maxtime), ) obj = { "txid": txid, "blockheight": tx.get("blockheight", None), "blockhash": tx.get("blockhash", None), "time": time, "blocktime": tx.get("blocktime", None), "conflicts": tx.get("walletconflicts", []), "bip125-replaceable": tx.get("bip125-replaceable", "no"), "hex": tx.get("hex", None), } txitem = self.ItemCls(self, self._addresses, self.rawdir, **obj) self[txid] = txitem if txitem.tx: for vout in txitem.tx.vout: try: addr = vout.script_pubkey.address( get_network(self.chain)) if addr not in addresses: addresses.append(addr) except: pass # maybe not an address, but a raw script? self._addresses.set_used(addresses) # detect category, amounts and addresses for tx in [self[txid] for txid in self if txid in txs]: self._fill_missing(tx) self._save()
def extract_xpub( self, derivation=None, device_type=None, path=None, fingerprint=None, passphrase="", chain="", ): with self._get_client( device_type=device_type, fingerprint=fingerprint, path=path, passphrase=passphrase, chain=chain, ) as client: # Client will be configured for testnet if our Specter instance is # currently connected to testnet. This will prevent us from # getting mainnet xpubs unless we set is_testnet here: try: client.chain = (Chain.TEST if derivation.split("/")[2].startswith("1") else Chain.MAIN) except: client.chain = Chain.MAIN network = networks.get_network("main" if client.chain == Chain.MAIN else "test") master_fpr = client.get_master_fingerprint().hex() try: xpub = client.get_pubkey_at_path(derivation).to_string() slip132_prefix = bip32.detect_version(derivation, default="xpub", network=network) xpub = convert_xpub_prefix(xpub, slip132_prefix) return "[{}/{}]{}\n".format(master_fpr, derivation.split("m/")[1], xpub) except Exception as e: logger.warning( f"Failed to import Nested Segwit singlesig mainnet key. Error: {e}" )
def fill_external_wallet_derivations(psbt, wallet): # fill derivations for receiving wallets if they own address net = get_network(wallet.manager.chain) wallets = wallet.manager.wallets.values() for out in psbt.outputs: try: # if we fail - not a big deal # check if output derivation is empty if out.bip32_derivations: continue try: # if there is no address representation - continue addr = out.script_pubkey.address(net) except: continue for w in wallets: # skip sending wallet if w == wallet: continue if not w.is_address_mine(addr): continue # we get here if wallet owns address info = w.get_address_info(addr) derivation = [int(info.change), int(info.index)] # we first one key and build derivation for it # it's enough for DIY to do the res k = w.keys[0] key = bip32.HDKey.from_string(k.xpub) if k.fingerprint != "": fingerprint = bytes.fromhex(k.fingerprint) else: fingerprint = key.my_fingerprint if k.derivation != "": der = bip32.parse_path(k.derivation) else: der = [] pub = key.derive(derivation).key out.bip32_derivations[pub] = DerivationPath( fingerprint, der + derivation) break except Exception as e: logger.exception(e)
def network_parameters(self): try: return self._network_parameters except Exception: return get_network("main")
def check_info(self): self._is_configured = self.rpc is not None self._is_running = False if self.rpc is not None and self.rpc.test_connection(): try: res = [ r["result"] for r in self.rpc.multi([ ("getblockchaininfo", None), ("getnetworkinfo", None), ("getmempoolinfo", None), ("uptime", None), ("getblockhash", 0), ("scantxoutset", "status", []), ]) ] self._info = res[0] self._network_info = res[1] self._info["mempool_info"] = res[2] self._info["uptime"] = res[3] try: self.rpc.getblockfilter(res[4]) self._info["blockfilterindex"] = True except: self._info["blockfilterindex"] = False self._info["utxorescan"] = (res[5]["progress"] if res[5] is not None and "progress" in res[5] else None) if self._info["utxorescan"] is None: self.utxorescanwallet = None self._network_parameters = get_network(self.chain) self._is_running = True except Exception as e: self._info = {"chain": None} self._network_info = {"subversion": "", "version": 999999} self._network_parameters = get_network("main") logger.error( f"connection {self.rpc} could not suceed check_info") logger.exception("Exception %s while check_info()" % e) else: if self.rpc is None: logger.warning(f"connection of {self} is None in check_info") elif not self.rpc.test_connection(): logger.debug( f"connection {self.rpc} failed test_connection in check_info:" ) try: self.rpc.multi([ ("getblockchaininfo", None), ("getnetworkinfo", None), ("getmempoolinfo", None), ("uptime", None), ("getblockhash", 0), ("scantxoutset", "status", []), ]) except Exception as e: logger.error(e) self._info = {"chain": None} self._network_info = {"subversion": "", "version": 999999} if not self._is_running: self._info["chain"] = None
from embit.liquid.descriptor import LDescriptor as Descriptor from embit.descriptor.checksum import add_checksum from embit.bip32 import HDKey from embit.liquid.networks import get_network from embit.liquid.pset import PSET as PSBT from embit.liquid.transaction import LSIGHASH from embit.liquid.finalizer import finalize_psbt from embit.liquid.addresses import addr_decode from embit.ec import PrivateKey wallet_prefix = "test" + random.randint(0, 0xFFFFFFFF).to_bytes(4, 'big').hex() root = HDKey.from_string( "tprv8ZgxMBicQKsPf27gmh4DbQqN2K6xnXA7m7AeceqQVGkRYny3X49sgcufzbJcq4k5eaGZDMijccdDzvQga2Saqd78dKqN52QwLyqgY8apX3j" ) fgp = root.child(0).fingerprint.hex() net = get_network('elreg') def random_wallet_name(): return "test" + random.randint(0, 0xFFFFFFFF).to_bytes(4, 'big').hex() class PSETTest(TestCase): """Complete tests with Core on regtest - should catch problems with signing of transactions""" def sign_with_descriptor(self, d1, d2, root, selfblind=False): rpc = daemon.rpc wname = random_wallet_name() # to derive addresses desc1 = Descriptor.from_string(d1) desc2 = Descriptor.from_string(d2) # recv addr 2
def decoderawtransaction(self, tx): unblinded = self.unblindrawtransaction(tx)["hex"] obj = super().__getattr__("decoderawtransaction")(unblinded) try: # unblind the rest of outputs b = LTransaction.from_string(tx) mbpk = PrivateKey(bytes.fromhex(self.dumpmasterblindingkey())) net = get_network(self.getblockchaininfo().get("chain")) outputs = obj["vout"] datas = [] fee = 0 # search for datas encoded in rangeproofs for i, out in enumerate(b.vout): o = outputs[i] if isinstance(out.value, int): if "value" in o: assert o["value"] == round(out.value * 1e-8, 8) else: o["value"] = round(out.value * 1e-8, 8) if "asset" in o: assert o["asset"] == bytes(reversed( out.asset[-32:])).hex() else: o["asset"] = bytes(reversed(out.asset[-32:])).hex() try: o["scriptPubKey"]["addresses"] = [ liquid_address(out.script_pubkey, network=net) ] except: pass if out.script_pubkey.data == b"": # fee negative? fee -= out.value pk = slip77.blinding_key(mbpk, out.script_pubkey) try: res = out.unblind(pk.secret, message_length=1000) value, asset, vbf, abf, extra, min_value, max_value = res if "value" in o: assert o["value"] == round(value * 1e-8, 8) else: o["value"] = round(value * 1e-8, 8) if "asset" in o: assert o["asset"] == bytes(reversed(asset[-32:])).hex() else: o["asset"] = bytes(reversed(asset[-32:])).hex() try: o["scriptPubKey"]["addresses"] = [ liquid_address(out.script_pubkey, pk, network=net) ] except: pass if len(extra.rstrip(b"\x00")) > 0: datas.append(extra) except Exception as e: pass # should be changed with seed from tx tx = PSET(b) seed = tagged_hash("liquid/blinding_seed", mbpk.secret) txseed = tx.txseed(seed) pubkeys = {} for extra in datas: s = BytesIO(extra) while True: k = read_string(s) if len(k) == 0: break v = read_string(s) if k[0] == 1 and len(k) == 5: idx = int.from_bytes(k[1:], "little") pubkeys[idx] = v elif k == b"\x01\x00": txseed = v for i, out in enumerate(outputs): o = out if i in pubkeys and len(pubkeys[i]) in [33, 65]: nonce = tagged_hash("liquid/range_proof", txseed + i.to_bytes(4, "little")) if b.vout[i].ecdh_pubkey == PrivateKey(nonce).sec(): try: res = unblind( pubkeys[i], nonce, b.vout[i].witness.range_proof.data, b.vout[i].value, b.vout[i].asset, b.vout[i].script_pubkey, ) value, asset, vbf, abf, extra, min_value, max_value = res if "value" in o: assert o["value"] == round(value * 1e-8, 8) else: o["value"] = round(value * 1e-8, 8) if "asset" in o: assert o["asset"] == bytes( reversed(asset[-32:])).hex() else: o["asset"] = bytes(reversed(asset[-32:])).hex() try: o["scriptPubKey"]["addresses"] = [ liquid_address( b.vout[i].script_pubkey, PublicKey.parse(pubkeys[i]), network=net, ) ] except: pass except Exception as e: logger.warn( f"Failed at unblinding output {i}: {e}") else: logger.warn(f"Failed at unblinding: {e}") if fee != 0: obj["fee"] = round(-fee * 1e-8, 8) except Exception as e: logger.warn(f"Failed at unblinding transaction: {e}") return obj
def address(self, idx=None, network=None): if network is None: net = get_network("test" if self.testnet else "main") else: net = get_network(network) return self.scriptpubkey(idx).address(net)