def _unblind(self): if not self.descriptor.is_blinded: return b = self.tx mbpk = self.descriptor.blinding_key.key net = self.network values = [0 for out in b.vout] assets = [b"\xFF" * 32 for out in b.vout] datas = [] # search for datas encoded in rangeproofs for i, out in enumerate(b.vout): # unblinded if isinstance(out.value, int): values[i] = out.value assets[i] = out.asset continue pk = slip77.blinding_key(mbpk, out.script_pubkey) try: res = out.unblind(pk.secret, message_length=1000) value, asset, vbf, abf, extra, *_ = res if len(extra.rstrip(b"\x00")) > 0: datas.append(extra) values[i] = value assets[i] = asset except Exception as e: logger.warn(e) # TODO: remove, it's ok pass # to calculate blinding seed 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(b.vout): if out.witness.range_proof.is_empty: continue if i in pubkeys and len(pubkeys[i]) in [33, 65]: nonce = tagged_hash("liquid/range_proof", txseed + i.to_bytes(4, "little")) if out.ecdh_pubkey == PrivateKey(nonce).sec(): try: res = unblind( pubkeys[i], nonce, out.witness.range_proof.data, out.value, out.asset, out.script_pubkey, ) value, asset, vbf, abf, extra, min_value, max_value = res assets[i] = asset values[i] = value except Exception as e: logger.warn(f"Failed at unblinding output {i}: {e}") else: logger.warn(f"Failed at unblinding: {e}") for i, out in enumerate(b.vout): out.asset = assets[i] out.value = values[i] out.witness = TxOutWitness()
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