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: jmprint('validating this utxo: ' + str(u), "info") 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()) jmprint('claimed address: ' + addr, "info") res = jm_single().bc_interface.query_utxo_set([u]) if len(res) != 1 or None in res: jmprint("utxo not found on blockchain: " + str(u), "error") return False if res[0]['address'] != addr: jmprint("privkey corresponds to the wrong address for utxo: " + str(u), "error") jmprint("blockchain returned address: {}".format(res[0]['address']), "error") jmprint("your privkey gave this address: " + addr, "error") return False if retrieve: results.append((u, res[0]['value'])) jmprint('all utxos validated OK', "success") 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 test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount, segwit_amt, segwit_ins, o_ins): """Creates a wallet from which non-segwit inputs/ outputs can be created, constructs one or more p2wpkh in p2sh spendable utxos (by paying into the corresponding address) and tests spending them in combination. wallet_structure is in accordance with commontest.make_wallets, see docs there in_amt is the amount to pay into each address into the wallet (non-segwit adds) amount (in satoshis) is how much we will pay to the output address segwit_amt in BTC is the amount we will fund each new segwit address with segwit_ins is a list of input indices (where to place the funding segwit utxos) other_ins is a list of input indices (where to place the funding non-sw utxos) """ wallet = make_wallets(1, wallet_structure, in_amt, walletclass=Wallet)[0]['wallet'] jm_single().bc_interface.sync_wallet(wallet) other_ins = {} ctr = 0 for k, v in wallet.unspent.iteritems(): #only extract as many non-segwit utxos as we need; #doesn't matter which they are if ctr == len(o_ins): break other_ins[k] = (v["value"], wallet.get_key_from_addr(v["address"]), o_ins[ctr]) ctr += 1 ins_sw = {} for i in range(len(segwit_ins)): #build segwit ins from "deterministic-random" keys; #intended to be the same for each run with the same parameters seed = json.dumps( [i, wallet_structure, in_amt, amount, segwit_ins, other_ins]) priv = btc.sha256(seed) + "01" pub = btc.privtopub(priv) #magicbyte is testnet p2sh addr1 = btc.pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=196) print "got address for p2shp2wpkh: " + addr1 txid = jm_single().bc_interface.grab_coins(addr1, segwit_amt) #TODO - int cast, fix? ins_sw[get_utxo_from_txid(txid, addr1)] = (int(segwit_amt * 100000000), priv, segwit_ins[i]) #make_sign_and_push will sanity check the received amount is correct txid = make_sign_and_push(ins_sw, wallet, amount, other_ins) #will always be False if it didn't push. assert txid
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 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 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()