def _get_blinding_factors(self, txdetails, wally_tx): utxos = txdetails['used_utxos'] or txdetails['old_used_utxos'] for i, o in enumerate(txdetails['transaction_outputs']): o['wally_index'] = i blinded_outputs = [ o for o in txdetails['transaction_outputs'] if not o['is_fee'] ] for output in blinded_outputs: # TODO: the derivation dance # the following values are in display order, reverse them when converting to bytes output['assetblinder'] = os.urandom(32).hex() output['amountblinder'] = os.urandom(32).hex() endpoints = utxos + blinded_outputs values = [endpoint['satoshi'] for endpoint in endpoints] abfs = b''.join( bytes.fromhex(endpoint['assetblinder'])[::-1] for endpoint in endpoints) vbfs = b''.join( bytes.fromhex(endpoint['amountblinder'])[::-1] for endpoint in endpoints[:-1]) final_vbf = wally.asset_final_vbf(values, len(utxos), abfs, vbfs) blinded_outputs[-1]['amountblinder'] = final_vbf[::-1].hex() for o in blinded_outputs: asset_commitment = wally.asset_generator_from_bytes( bytes.fromhex(o['asset_id'])[::-1], bytes.fromhex(o['assetblinder'])[::-1]) value_commitment = wally.asset_value_commitment( o['satoshi'], bytes.fromhex(o['amountblinder'])[::-1], asset_commitment) o['asset_commitment'] = asset_commitment.hex() o['value_commitment'] = value_commitment.hex() # Write the commitments into the wally tx for signing wally.tx_set_output_asset(wally_tx, o['wally_index'], asset_commitment) wally.tx_set_output_value(wally_tx, o['wally_index'], value_commitment) retval = {} for key in [ 'assetblinders', 'amountblinders', 'asset_commitments', 'value_commitments' ]: # gdk expects to get an empty entry for the fee output too, hence this is over the # transaction outputs, not just the blinded outputs (fee will just have empty # strings) retval[key] = [ o.get(key[:-1], '') for o in txdetails['transaction_outputs'] ] return retval
def main(): parser = argparse.ArgumentParser() parser.add_argument( "-n", "--node-url", help="Elements node URL, eg http://USERNAME:PASSWORD@HOST:PORT/", required=True) parser.add_argument("-u", "--utxo", help="txid:vout", required=True) parser.add_argument("-a", "--asset", help="asset to receive", required=True) parser.add_argument("-r", "--rate", type=float, help="price_asset_send/price_asset_receive", required=True) args = parser.parse_args() txid, vout = args.utxo.split(":") vout = int(vout) asset_receive, rate = args.asset, args.rate connection = RPCHost(args.node_url) unspents = connection.call("listunspent") utxo = [u for u in unspents if u["txid"] == txid and u["vout"] == vout][0] amount_receive = round(rate * utxo["amount"], 8) address = connection.call("getnewaddress") tx = connection.call("createrawtransaction", [{ "txid": txid, "vout": vout, "sequence": 0xffffffff }], {address: amount_receive}, 0, False, {address: asset_receive}) asset_blinder_bytes = os.urandom(32) amount_blinder_bytes = os.urandom(32) asset_commitment = wally.asset_generator_from_bytes( h2b_rev(asset_receive), asset_blinder_bytes) amount_commitment = wally.asset_value_commitment(btc2sat(amount_receive), amount_blinder_bytes, asset_commitment) tx_ = wally.tx_from_hex( tx, wally.WALLY_TX_FLAG_USE_WITNESS | wally.WALLY_TX_FLAG_USE_ELEMENTS) wally.tx_set_output_asset(tx_, 0, asset_commitment) wally.tx_set_output_value(tx_, 0, amount_commitment) tx = wally.tx_to_hex( tx_, wally.WALLY_TX_FLAG_USE_WITNESS | wally.WALLY_TX_FLAG_USE_ELEMENTS) ret = connection.call("signrawtransactionwithwallet", tx, None, "SINGLE|ANYONECANPAY") assert ret["complete"] print( json.dumps( { "tx": ret["hex"], "inputs": [{ "asset": utxo["asset"], "amount": btc2sat(utxo["amount"]), "asset_blinder": utxo["assetblinder"], "amount_blinder": utxo["amountblinder"], }], "outputs": [{ "asset": asset_receive, "amount": btc2sat(amount_receive), "asset_blinder": b2h_rev(asset_blinder_bytes), "amount_blinder": b2h_rev(amount_blinder_bytes), }], }, separators=(',', ':')))