def parse_blinded_element(e): valid_key_sets = [ { "asset_id_hex", "asset_blinder_hex", "amount_satoshi", "amount_blinder_hex" }, {"asset_id_hex", "asset_blinder_hex"}, {"amount_satoshi", "amount_blinder_hex", "asset_commitment_hex"}, ] if not isinstance(e, dict): err("Should be a dict") if set(e.keys()) not in valid_key_sets: err("Invalid keys") if "asset_id_hex" in e: asset_commitment_bin = wally.asset_generator_from_bytes( parse_uint256_hex(e["asset_id_hex"]), parse_uint256_hex(e["asset_blinder_hex"])) else: asset_commitment_bin = parse_commitment_hex(e["asset_commitment_hex"]) if "amount_satoshi" in e: amount_commitment_bin = wally.asset_value_commitment( parse_sat(e["amount_satoshi"]), parse_uint256_hex(e["amount_blinder_hex"]), asset_commitment_bin) else: amount_commitment_bin = None return { "asset_id_hex": e.get("asset_id_hex"), "asset_commitment_bin": asset_commitment_bin, "amount_satoshi": e.get("amount_satoshi"), "amount_commitment_bin": amount_commitment_bin, }
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 parse_blinded_element_v0(e): asset_commitment_bin = None amount_commitment_bin = None if len(e.keys()) == 5: asset_commitment_bin = wally.asset_generator_from_bytes( parse_uint256_hex(e["asset_id"]), parse_uint256_hex(e["assetblinder"])) amount_commitment_bin = wally.asset_value_commitment( parse_sat(e["satoshi"]), parse_uint256_hex(e["amountblinder"]), asset_commitment_bin) return { "asset_id_hex": e.get("asset_id"), "asset_commitment_bin": asset_commitment_bin, "amount_satoshi": e.get("satoshi"), "amount_commitment_bin": amount_commitment_bin, }
locktime = 0 output_tx = wally.tx_init(version, locktime, 0, 0) # end-create_output_tx # start-create_outputs for value, blinding_pubkey, script_pubkey in zip(output_values, blinding_pubkeys, script_pubkeys): abf, abfs_out = abfs_out[:32], abfs_out[32:] vbf, vbfs_out = vbfs_out[:32], vbfs_out[32:] asset_id, output_asset_ids = output_asset_ids[:32], output_asset_ids[32:] generator = wally.asset_generator_from_bytes(asset_id, abf) value_commitment = wally.asset_value_commitment(value, vbf, generator) ephemeral_privkey = os.urandom(32) ephemeral_pubkey = wally.ec_public_key_from_private_key(ephemeral_privkey) rangeproof = wally.asset_rangeproof( value, blinding_pubkey, ephemeral_privkey, asset_id, abf, vbf, value_commitment, script_pubkey, generator, 1, # min_value
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=(',', ':')))