def get_imported_privkey_branch(wallet, m, showprivkey): if m in wallet.imported_privkeys: entries = [] for i, privkey in enumerate(wallet.imported_privkeys[m]): pub = btc.privkey_to_pubkey(privkey) addr = btc.pubkey_to_p2sh_p2wpkh_address( pub, magicbyte=get_p2sh_vbyte()) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] used = ('used' if balance > 0.0 else 'empty') if showprivkey: wip_privkey = btc.wif_compressed_privkey( privkey, get_p2pk_vbyte()) else: wip_privkey = '' entries.append( WalletViewEntry("m/0", m, -1, i, addr, [balance, balance], used=used, priv=wip_privkey)) return WalletViewBranch("m/0", m, -1, branchentries=entries) return None
def on_JM_TX_RECEIVED(self, nick, txhex, offer): # "none" flags p2ep protocol; pass through to the generic # on_tx handler for that: if offer == "none": return self.on_p2ep_tx_received(nick, txhex) offer = json.loads(offer) retval = self.client.on_tx_received(nick, txhex, offer) if not retval[0]: jlog.info("Maker refuses to continue on receipt of tx") else: sigs = retval[1] self.finalized_offers[nick] = offer tx = btc.deserialize(txhex) self.finalized_offers[nick]["txd"] = tx jm_single().bc_interface.add_tx_notify( tx, self.unconfirm_callback, self.confirm_callback, offer["cjaddr"], wallet_name=jm_single().bc_interface.get_wallet_name( self.client.wallet), txid_flag=False, vb=get_p2sh_vbyte()) d = self.callRemote(commands.JMTXSigs, nick=nick, sigs=json.dumps(sigs)) self.defaultCallbacks(d) return {"accepted": True}
def wallet_importprivkey(wallet, mixdepth): print('WARNING: This imported key will not be recoverable with your 12 ' + 'word mnemonic phrase. Make sure you have backups.') print('WARNING: Handling of raw ECDSA bitcoin private keys can lead to ' 'non-intuitive behaviour and loss of funds.\n Recommended instead ' 'is to use the \'sweep\' feature of sendpayment.py ') privkeys = raw_input('Enter private key(s) to import: ') privkeys = privkeys.split(',') if ',' in privkeys else privkeys.split() # TODO read also one key for each line for privkey in privkeys: # TODO is there any point in only accepting wif format? check what # other wallets do privkey_bin = btc.from_wif_privkey(privkey, vbyte=get_p2sh_vbyte()).decode('hex')[:-1] encrypted_privkey = encryptData(wallet.password_key, privkey_bin) if 'imported_keys' not in wallet.walletdata: wallet.walletdata['imported_keys'] = [] wallet.walletdata['imported_keys'].append( {'encrypted_privkey': encrypted_privkey.encode('hex'), 'mixdepth': mixdepth}) if wallet.walletdata['imported_keys']: fd = open(wallet.path, 'w') fd.write(json.dumps(wallet.walletdata)) fd.close() print('Private key(s) successfully imported')
def validate_utxo_data(utxo_datas, retrieve=False, segwit=False): """For each txid: N, privkey, first convert the privkey and convert to address, then use the blockchain instance to look up the utxo and check that its address field matches. If retrieve is True, return the set of utxos and their values. """ results = [] for u, priv in utxo_datas: print('validating this utxo: ' + str(u)) hexpriv = btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()) if segwit: addr = btc.pubkey_to_p2sh_p2wpkh_address( btc.privkey_to_pubkey(hexpriv), get_p2sh_vbyte()) else: addr = btc.privkey_to_address(hexpriv, magicbyte=get_p2pk_vbyte()) print('claimed address: ' + addr) res = jm_single().bc_interface.query_utxo_set([u]) print('blockchain shows this data: ' + str(res)) if len(res) != 1 or None in res: print("utxo not found on blockchain: " + str(u)) return False if res[0]['address'] != addr: print("privkey corresponds to the wrong address for utxo: " + str(u)) print("blockchain returned address: {}".format(res[0]['address'])) print("your privkey gave this address: " + addr) return False if retrieve: results.append((u, res[0]['value'])) print('all utxos validated OK') if retrieve: return results return True
def build_outs_from_template(self): """Build the outputs for this transaction based on the template, which must exist due to the constructor. Unlike build_ins, this has no dependencies but constructs appropriate output scripts based on the text flag "p2sh-p2wpkh" or "NN" in the template. It is necessary to fill out the keys template self.keys, before calling this. """ for i, t in enumerate(self.template.outs): if t.spk_type == "p2sh-p2wpkh": self.outs.append({ "address": btc.pubkey_to_p2sh_p2wpkh_address( self.keys["outs"][i][t.counterparty], get_p2sh_vbyte()), "value": t.amount }) elif t.spk_type == "NN": #check if all the necessary keys are available if not all([ j in self.keys["outs"][i] for j in range(self.n_counterparties) ]): raise Exception("Incomplete key data to construct outputs") self.outs.append({ "address": btc.pubkeys_to_p2wsh_address(self.keys["outs"][i].values(), vbyte=100), "value": t.amount })
def wallet_dumpprivkey(wallet, hdpath): pathlist = bip32pathparse(hdpath) print('got pathlist: ' + str(pathlist)) if pathlist and len(pathlist) == 5: cointype, purpose, m, forchange, k = pathlist key = wallet.get_key(m, forchange, k) wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2sh_vbyte()) return wifkey else: return hdpath + " is not a valid hd wallet path"
def msig_data_from_pubkeys(pubkeys, N): """Create a p2sh address for the list of pubkeys given, N signers required. Return both the multisig redeem script and the p2sh address created. Order of pubkeys is respected (see TODO). """ #TODO: lexicographical ordering is better multisig_script = btc.mk_multisig_script(pubkeys, N) p2sh_address = btc.p2sh_scriptaddr(multisig_script, magicbyte=get_p2sh_vbyte()) return (multisig_script, p2sh_address)
def wallet_signmessage(wallet, hdpath, message): if hdpath.startswith(wallet.get_root_path()): m, forchange, k = [int(y) for y in hdpath[4:].split('/')] key = wallet.get_key(m, forchange, k) addr = btc.privkey_to_address(key, magicbyte=get_p2sh_vbyte()) print('Using address: ' + addr) else: print('%s is not a valid hd wallet path' % hdpath) return None sig = btc.ecdsa_sign(message, key, formsg=True) retval = "Signature: " + str(sig) + "\n" retval += "To verify this in Bitcoin Core use the RPC command 'verifymessage'" return retval
def wallet_display(wallet, gaplimit, showprivkey, displayall=False, serialized=True): """build the walletview object, then return its serialization directly if serialized, else return the WalletView object. """ acctlist = [] rootpath = wallet.get_root_path() for m in range(wallet.max_mix_depth): branchlist = [] for forchange in [0, 1]: entrylist = [] if forchange == 0: xpub_key = btc.bip32_privtopub(wallet.keys[m][forchange]) else: xpub_key = "" for k in range(wallet.index[m][forchange] + gaplimit): addr = wallet.get_addr(m, forchange, k) balance = 0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] used = 'used' if k < wallet.index[m][forchange] else 'new' if showprivkey: privkey = btc.wif_compressed_privkey( wallet.get_key(m, forchange, k), get_p2sh_vbyte()) else: privkey = '' if (displayall or balance > 0 or (used == 'new' and forchange == 0)): entrylist.append(WalletViewEntry(rootpath, m, forchange, k, addr, [balance, balance], priv=privkey, used=used)) branchlist.append(WalletViewBranch(rootpath, m, forchange, entrylist, xpub=xpub_key)) ipb = get_imported_privkey_branch(wallet, m, showprivkey) if ipb: branchlist.append(ipb) #get the xpub key of the whole account xpub_account = btc.bip32_privtopub( wallet.get_mixing_depth_keys(wallet.get_master_key())[m]) acctlist.append(WalletViewAccount(rootpath, m, branchlist, xpub=xpub_account)) walletview = WalletView(rootpath, acctlist) if serialized: return walletview.serialize() else: return walletview
def query_utxo_set(self, txouts,includeconf=False): if self.qusfail: #simulate failure to find the utxo return [None] if self.fake_query_results: result = [] for x in self.fake_query_results: for y in txouts: if y == x['utxo']: result.append(x) return result result = [] #external maker utxos known_outs = {"03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6:1": "03a2d1cbe977b1feaf8d0d5cc28c686859563d1520b28018be0c2661cf1ebe4857", "498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3:0": "02b4b749d54e96b04066b0803e372a43d6ffa16e75a001ae0ed4b235674ab286be", "3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c:1": "023bcbafb4f68455e0d1d117c178b0e82a84e66414f0987453d78da034b299c3a9"} #our wallet utxos, faked, for podle tests: utxos are doctored (leading 'f'), #and the lists are (amt, age) wallet_outs = {'f34b635ed8891f16c4ec5b8236ae86164783903e8e8bb47fa9ef2ca31f3c2d7a:0': [10000000, 2], 'f780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1': [20000000, 6], 'fe574db96a4d43a99786b3ea653cda9e4388f377848f489332577e018380cff1:0': [50000000, 3], 'fd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0': [50000000, 6]} if includeconf and set(txouts).issubset(set(wallet_outs)): #includeconf used as a trigger for a podle check; #here we simulate a variety of amount/age returns results = [] for to in txouts: results.append({'value': wallet_outs[to][0], 'confirms': wallet_outs[to][1]}) return results if txouts[0] in known_outs: addr = btc.pubkey_to_p2sh_p2wpkh_address( known_outs[txouts[0]], get_p2sh_vbyte()) return [{'value': 200000000, 'address': addr, 'script': btc.address_to_script(addr), 'confirms': 20}] for t in txouts: result_dict = {'value': 10000000000, 'address': "mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ", 'script': '76a91479b000887626b294a914501a4cd226b58b23598388ac'} if includeconf: result_dict['confirms'] = 20 result.append(result_dict) return result
def create_recipient_address(bob_pubkey, tweak=None, segwit=False): """Create a p2pkh receiving address from an existing pubkey, tweaked by a random 32 byte scalar. Returns the tweak, the new pubkey point and the new address. The recipient can set the tweak parameter. """ if not tweak: tweak = binascii.hexlify(os.urandom(32)) tweak_point = btc.privkey_to_pubkey(tweak + "01") destination_point = btc.add_pubkeys([tweak_point, bob_pubkey], True) if segwit: destination_address = btc.pubkey_to_p2sh_p2wpkh_address( destination_point, magicbyte=get_p2sh_vbyte()) else: destination_address = btc.pubkey_to_address(destination_point, magicbyte=get_p2pk_vbyte()) return (tweak, destination_point, destination_address)
def final_checks(self): """Check that our keys have received the right funds in the wallet (all the single-owned outpoints to p2sh-p2wpkh outpoints should contain utxos that own the intended number of coins). """ match = True total_coins = 0 for i, tx in enumerate(self.template.txs): txid = self.realtxs[i].txid for j, tout in enumerate(tx.outs): if tout.counterparty == 0: expected_amount = tout.amount print("We expected this amount out: ", expected_amount) actual_key = self.realtxs[i].keys["outs"][j][0] actual_address = btc.pubkey_to_p2sh_p2wpkh_address( actual_key, get_p2sh_vbyte()) #direct query on blockchain for the transaction, #then check if it pays to our address and in what amount res = cjxt_single().bc_interface.rpc( 'gettxout', [txid, j, True]) if not ("scriptPubKey" in res and "addresses" in res["scriptPubKey"]): print("Failed to query the tx: ", txid) found_address = str(res["scriptPubKey"]["addresses"][0]) if not found_address == actual_address: print("Error, transaction, vout: ", txid, j, "has address: ", found_address, ", but should have been address: ", actual_address) print("Amount received was: ", res["value"], " at address: ", actual_address) sat_value = btc_to_satoshis(res["value"]) total_coins += res["value"] if not sat_value == expected_amount or not actual_address == found_address: match = False if match: print("Success! Received back total coins: ", total_coins) else: print( "Failure! Not all expected coins received, see above summary.") reactor.stop()
def wallet_showutxos(wallet, showprivkey): unsp = {} max_tries = jm_single().config.getint("POLICY", "taker_utxo_retries") for u, av in wallet.unspent.iteritems(): key = wallet.get_key_from_addr(av['address']) tries = podle.get_podle_tries(u, key, max_tries) tries_remaining = max(0, max_tries - tries) unsp[u] = {'address': av['address'], 'value': av['value'], 'tries': tries, 'tries_remaining': tries_remaining, 'external': False} if showprivkey: wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2sh_vbyte()) unsp[u]['privkey'] = wifkey used_commitments, external_commitments = podle.get_podle_commitments() for u, ec in external_commitments.iteritems(): tries = podle.get_podle_tries(utxo=u, max_tries=max_tries, external=True) tries_remaining = max(0, max_tries - tries) unsp[u] = {'tries': tries, 'tries_remaining': tries_remaining, 'external': True} return json.dumps(unsp, indent=4)
def wallet_fetch_history(wallet, options): # sort txes in a db because python can be really bad with large lists con = sqlite3.connect(":memory:") con.row_factory = sqlite3.Row tx_db = con.cursor() tx_db.execute("CREATE TABLE transactions(txid TEXT, " "blockhash TEXT, blocktime INTEGER);") jm_single().debug_silence[0] = True wallet_name = jm_single().bc_interface.get_wallet_name(wallet) for wn in [wallet_name, ""]: buf = range(1000) t = 0 while len(buf) == 1000: buf = jm_single().bc_interface.rpc('listtransactions', [wn, 1000, t, True]) t += len(buf) tx_data = ((tx['txid'], tx['blockhash'], tx['blocktime']) for tx in buf if 'txid' in tx and 'blockhash' in tx and 'blocktime' in tx) tx_db.executemany('INSERT INTO transactions VALUES(?, ?, ?);', tx_data) txes = tx_db.execute('SELECT DISTINCT txid, blockhash, blocktime ' 'FROM transactions ORDER BY blocktime').fetchall() wallet_addr_cache = wallet.addr_cache wallet_addr_set = set(wallet_addr_cache.keys()) def s(): return ',' if options.csv else ' ' def sat_to_str(sat): return '%.8f'%(sat/1e8) def sat_to_str_p(sat): return '%+.8f'%(sat/1e8) def skip_n1(v): return '% 2s'%(str(v)) if v != -1 else ' #' def skip_n1_btc(v): return sat_to_str(v) if v != -1 else '#' + ' '*10 def print_row(index, time, tx_type, amount, delta, balance, cj_n, miner_fees, utxo_count, mixdepth_src, mixdepth_dst, txid): data = [index, datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M"), tx_type, sat_to_str(amount), sat_to_str_p(delta), sat_to_str(balance), skip_n1(cj_n), sat_to_str(miner_fees), '% 3d' % utxo_count, skip_n1(mixdepth_src), skip_n1(mixdepth_dst)] if options.verbosity % 2 == 0: data += [txid] print(s().join(map('"{}"'.format, data))) field_names = ['tx#', 'timestamp', 'type', 'amount/btc', 'balance-change/btc', 'balance/btc', 'coinjoin-n', 'total-fees', 'utxo-count', 'mixdepth-from', 'mixdepth-to'] if options.verbosity % 2 == 0: field_names += ['txid'] if options.csv: print('Bumping verbosity level to 4 due to --csv flag') options.verbosity = 1 if options.verbosity > 0: print(s().join(field_names)) if options.verbosity <= 2: cj_batch = [0]*8 + [[]]*2 balance = 0 utxo_count = 0 deposits = [] deposit_times = [] for i, tx in enumerate(txes): rpctx = jm_single().bc_interface.rpc('gettransaction', [tx['txid']]) txhex = str(rpctx['hex']) txd = btc.deserialize(txhex) output_addr_values = dict(((btc.script_to_address(sv['script'], get_p2sh_vbyte()), sv['value']) for sv in txd['outs'])) our_output_addrs = wallet_addr_set.intersection( output_addr_values.keys()) from collections import Counter value_freq_list = sorted(Counter(output_addr_values.values()) .most_common(), key=lambda x: -x[1]) non_cj_freq = 0 if len(value_freq_list)==1 else sum(zip( *value_freq_list[1:])[1]) is_coinjoin = (value_freq_list[0][1] > 1 and value_freq_list[0][1] in [non_cj_freq, non_cj_freq+1]) cj_amount = value_freq_list[0][0] cj_n = value_freq_list[0][1] rpc_inputs = [] for ins in txd['ins']: try: wallet_tx = jm_single().bc_interface.rpc('gettransaction', [ins['outpoint']['hash']]) except JsonRpcError: continue input_dict = btc.deserialize(str(wallet_tx['hex']))['outs'][ins[ 'outpoint']['index']] rpc_inputs.append(input_dict) rpc_input_addrs = set((btc.script_to_address(ind['script'], get_p2sh_vbyte()) for ind in rpc_inputs)) our_input_addrs = wallet_addr_set.intersection(rpc_input_addrs) our_input_values = [ind['value'] for ind in rpc_inputs if btc. script_to_address(ind['script'], get_p2sh_vbyte()) in our_input_addrs] our_input_value = sum(our_input_values) utxos_consumed = len(our_input_values) tx_type = None amount = 0 delta_balance = 0 fees = 0 mixdepth_src = -1 mixdepth_dst = -1 #TODO this seems to assume all the input addresses are from the same # mixdepth, which might not be true if len(our_input_addrs) == 0 and len(our_output_addrs) > 0: #payment to us amount = sum([output_addr_values[a] for a in our_output_addrs]) tx_type = 'deposit ' cj_n = -1 delta_balance = amount mixdepth_dst = tuple(wallet_addr_cache[a][0] for a in our_output_addrs) if len(mixdepth_dst) == 1: mixdepth_dst = mixdepth_dst[0] elif len(our_input_addrs) == 0 and len(our_output_addrs) == 0: continue # skip those that don't belong to our wallet elif len(our_input_addrs) > 0 and len(our_output_addrs) == 0: # we swept coins elsewhere if is_coinjoin: tx_type = 'cj sweepout' amount = cj_amount fees = our_input_value - cj_amount else: tx_type = 'sweep out ' amount = sum([v for v in output_addr_values.values()]) fees = our_input_value - amount delta_balance = -our_input_value mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0] elif len(our_input_addrs) > 0 and len(our_output_addrs) == 1: # payment to somewhere with our change address getting the remaining change_value = output_addr_values[list(our_output_addrs)[0]] if is_coinjoin: tx_type = 'cj withdraw' amount = cj_amount else: tx_type = 'withdraw' #TODO does tx_fee go here? not my_tx_fee only? amount = our_input_value - change_value cj_n = -1 delta_balance = change_value - our_input_value fees = our_input_value - change_value - cj_amount mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0] elif len(our_input_addrs) > 0 and len(our_output_addrs) == 2: #payment to self out_value = sum([output_addr_values[a] for a in our_output_addrs]) if not is_coinjoin: print('this is wrong TODO handle non-coinjoin internal') tx_type = 'cj internal' amount = cj_amount delta_balance = out_value - our_input_value mixdepth_src = wallet_addr_cache[list(our_input_addrs)[0]][0] cj_addr = list(set([a for a,v in output_addr_values.iteritems() if v == cj_amount]).intersection(our_output_addrs))[0] mixdepth_dst = wallet_addr_cache[cj_addr][0] else: tx_type = 'unknown type' print('our utxos: ' + str(len(our_input_addrs)) \ + ' in, ' + str(len(our_output_addrs)) + ' out') balance += delta_balance utxo_count += (len(our_output_addrs) - utxos_consumed) index = '% 4d'%(i) timestamp = datetime.fromtimestamp(rpctx['blocktime'] ).strftime("%Y-%m-%d %H:%M") utxo_count_str = '% 3d' % (utxo_count) if options.verbosity > 0: if options.verbosity <= 2: n = cj_batch[0] if tx_type == 'cj internal': cj_batch[0] += 1 cj_batch[1] += rpctx['blocktime'] cj_batch[2] += amount cj_batch[3] += delta_balance cj_batch[4] = balance cj_batch[5] += cj_n cj_batch[6] += fees cj_batch[7] += utxo_count cj_batch[8] += [mixdepth_src] cj_batch[9] += [mixdepth_dst] elif tx_type != 'unknown type': if n > 0: # print the previously-accumulated batch print_row('N='+"%2d"%n, cj_batch[1]/n, 'cj batch ', cj_batch[2], cj_batch[3], cj_batch[4], cj_batch[5]/n, cj_batch[6], cj_batch[7]/n, min(cj_batch[8]), max(cj_batch[9]), '...') cj_batch = [0]*8 + [[]]*2 # reset the batch collector # print batch terminating row print_row(index, rpctx['blocktime'], tx_type, amount, delta_balance, balance, cj_n, fees, utxo_count, mixdepth_src, mixdepth_dst, tx['txid']) elif options.verbosity >= 5 or \ (options.verbosity >= 3 and tx_type != 'unknown type'): print_row(index, rpctx['blocktime'], tx_type, amount, delta_balance, balance, cj_n, fees, utxo_count, mixdepth_src, mixdepth_dst, tx['txid']) if tx_type != 'cj internal': deposits.append(delta_balance) deposit_times.append(rpctx['blocktime']) # we could have a leftover batch! if options.verbosity <= 2: n = cj_batch[0] if n > 0: print_row('N='+"%2d"%n, cj_batch[1]/n, 'cj batch ', cj_batch[2], cj_batch[3], cj_batch[4], cj_batch[5]/n, cj_batch[6], cj_batch[7]/n, min(cj_batch[8]), max(cj_batch[9]), '...') bestblockhash = jm_single().bc_interface.rpc('getbestblockhash', []) try: #works with pruning enabled, but only after v0.12 now = jm_single().bc_interface.rpc('getblockheader', [bestblockhash] )['time'] except JsonRpcError: now = jm_single().bc_interface.rpc('getblock', [bestblockhash])['time'] print(' %s best block is %s' % (datetime.fromtimestamp(now) .strftime("%Y-%m-%d %H:%M"), bestblockhash)) print('total profit = ' + str(float(balance - sum(deposits)) / float(100000000)) + ' BTC') try: # https://gist.github.com/chris-belcher/647da261ce718fc8ca10 import numpy as np from scipy.optimize import brentq deposit_times = np.array(deposit_times) now -= deposit_times[0] deposit_times -= deposit_times[0] deposits = np.array(deposits) def f(r, deposits, deposit_times, now, final_balance): return np.sum(np.exp((now - deposit_times) / 60.0 / 60 / 24 / 365)**r * deposits) - final_balance r = brentq(f, a=1, b=-1, args=(deposits, deposit_times, now, balance)) print('continuously compounded equivalent annual interest rate = ' + str(r * 100) + ' %') print('(as if yield generator was a bank account)') except ImportError: print('numpy/scipy not installed, unable to calculate effective ' + 'interest rate') total_wallet_balance = sum(wallet.get_balance_by_mixdepth().values()) if balance != total_wallet_balance: print(('BUG ERROR: wallet balance (%s) does not match balance from ' + 'history (%s)') % (sat_to_str(total_wallet_balance), sat_to_str(balance))) if utxo_count != len(wallet.unspent): print(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' + 'history (%s)') % (len(wallet.unspent), utxo_count))
def get_vbyte(self): return get_p2sh_vbyte()
def address_p2sh_generator(): return get_address_generator(b'\xa9\x14', b'\x87', get_p2sh_vbyte())
def scan_for_coinjoins(privkey, amount, filename): """Given a file which contains encrypted coinjoin proposals, and a private key for a pubkey with a known utxo existing which we can spend, scan the entries in the file, all assumed to be ECIES encrypted to a pubkey, for one which is encrypted to *this* pubkey, if found, output the retrieved partially signed transaction, and destination key, address to a list which is returned to the caller. Only if the retrieved coinjoin transaction passes basic checks on validity in terms of amount paid, is it returned. This is an elementary implementation that will obviously fail any performance test (i.e. moderately large lists). Note that the tweaked output address must be of type p2sh/p2wpkh. """ try: with open(filename, "rb") as f: msgs = f.readlines() except: print("Failed to read from file: ", filename) return valid_coinjoins = [] for msg in msgs: try: decrypted_msg = decrypt_message(msg, privkey) tweak, tx = deserialize_coinjoin_proposal(decrypted_msg) except: print("Could not decrypt message, skipping") continue if not tweak: print("Could not decrypt message, reason: " + str(tx)) continue #We analyse the content of the transaction to check if it follows #our requirements try: deserialized_tx = btc.deserialize(tx) except: print("Proposed transaction is not correctly formatted, skipping.") continue #construct our receiving address according to the tweak pubkey = btc.privkey_to_pubkey(privkey) tweak, destnpt, my_destn_addr = create_recipient_address(pubkey, tweak=tweak, segwit=True) #add_privkeys requires both inputs to be compressed (or un-) consistently. tweak_priv = tweak + "01" my_destn_privkey = btc.add_privkeys(tweak_priv, privkey, True) my_output_index = -1 for i, o in enumerate(deserialized_tx['outs']): addr = btc.script_to_address(o['script'], get_p2sh_vbyte()) if addr == my_destn_addr: print('found our output address: ', my_destn_addr) my_output_index = i break if my_output_index == -1: print("Proposal doesn't contain our output address, rejecting") continue my_output_amount = deserialized_tx['outs'][i]['value'] required_amount = amount - 2 * estimate_tx_fee(3, 3, 'p2sh-p2wpkh') if my_output_amount < required_amount: print("Proposal pays too little, difference is: ", required_amount - my_output_amount) continue #now we know output is acceptable to us, we should check that the #ctrprty input is signed and the other input is ours, but will do this #later; if it's not, it just won't work so NBD for now. valid_coinjoins.append((my_destn_addr, my_destn_privkey, tx)) return valid_coinjoins