def test_commitment_retries(setup_podle): """Assumes no external commitments available. Generate pretend priv/utxo pairs and check that they can be used taker_utxo_retries times. """ allowed = jm_single().config.getint("POLICY", "taker_utxo_retries") #make some pretend commitments dummy_priv_utxo_pairs = [(btc.sha256(os.urandom(10)), btc.sha256(os.urandom(10))+":0") for _ in range(10)] #test a single commitment request of all 10 for x in dummy_priv_utxo_pairs: p = btc.generate_podle([x], allowed) assert p #At this point slot 0 has been taken by all 10. for i in range(allowed-1): p = btc.generate_podle(dummy_priv_utxo_pairs[:1], allowed) assert p p = btc.generate_podle(dummy_priv_utxo_pairs[:1], allowed) assert p is None
def test_commitment_retries(setup_podle): """Assumes no external commitments available. Generate pretend priv/utxo pairs and check that they can be used taker_utxo_retries times. """ allowed = jm_single().config.getint("POLICY", "taker_utxo_retries") #make some pretend commitments dummy_priv_utxo_pairs = [(btc.sha256(os.urandom(10)), btc.sha256(os.urandom(10)) + ":0") for _ in range(10)] #test a single commitment request of all 10 for x in dummy_priv_utxo_pairs: p = btc.generate_podle([x], allowed) assert p #At this point slot 0 has been taken by all 10. for i in range(allowed - 1): p = btc.generate_podle(dummy_priv_utxo_pairs[:1], allowed) assert p p = btc.generate_podle(dummy_priv_utxo_pairs[:1], allowed) assert p is None
def make_commitment(self, wallet, input_utxos, cjamount): """The Taker default commitment function, which uses PoDLE. Alternative commitment types should use a different commit type byte. This will allow future upgrades to provide different style commitments by subclassing Taker and changing the commit_type_byte; existing makers will simply not accept this new type of commitment. In case of success, return the commitment and its opening. In case of failure returns (None, None) and constructs a detailed log for the user to read and discern the reason. """ def filter_by_coin_age_amt(utxos, age, amt): results = jm_single().bc_interface.query_utxo_set(utxos, includeconf=True) newresults = [] too_old = [] too_small = [] for i, r in enumerate(results): #results return "None" if txo is spent; drop this if not r: continue valid_age = r['confirms'] >= age valid_amt = r['value'] >= amt if not valid_age: too_old.append(utxos[i]) if not valid_amt: too_small.append(utxos[i]) if valid_age and valid_amt: newresults.append(utxos[i]) return newresults, too_old, too_small def priv_utxo_pairs_from_utxos(utxos, age, amt): #returns pairs list of (priv, utxo) for each valid utxo; #also returns lists "too_old" and "too_small" for any #utxos that did not satisfy the criteria for debugging. priv_utxo_pairs = [] new_utxos, too_old, too_small = filter_by_coin_age_amt( utxos.keys(), age, amt) new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos} for k, v in new_utxos_dict.iteritems(): addr = v['address'] priv = wallet.get_key_from_addr(addr) if priv: #can be null from create-unsigned priv_utxo_pairs.append((priv, k)) return priv_utxo_pairs, too_old, too_small commit_type_byte = "P" podle_data = None tries = jm_single().config.getint("POLICY", "taker_utxo_retries") age = jm_single().config.getint("POLICY", "taker_utxo_age") #Minor rounding errors don't matter here amt = int( cjamount * jm_single().config.getint("POLICY", "taker_utxo_amtpercent") / 100.0) priv_utxo_pairs, to, ts = priv_utxo_pairs_from_utxos( input_utxos, age, amt) #Note that we ignore the "too old" and "too small" lists in the first #pass through, because the same utxos appear in the whole-wallet check. #For podle data format see: btc.podle.PoDLE.reveal() #In first round try, don't use external commitments podle_data = btc.generate_podle(priv_utxo_pairs, tries) if not podle_data: #We defer to a second round to try *all* utxos in wallet; #this is because it's much cleaner to use the utxos involved #in the transaction, about to be consumed, rather than use #random utxos that will persist after. At this step we also #allow use of external utxos in the json file. if wallet.unspent: priv_utxo_pairs, to, ts = priv_utxo_pairs_from_utxos( wallet.unspent, age, amt) #Pre-filter the set of external commitments that work for this #transaction according to its size and age. dummy, extdict = btc.get_podle_commitments() if len(extdict.keys()) > 0: ext_valid, ext_to, ext_ts = filter_by_coin_age_amt( extdict.keys(), age, amt) else: ext_valid = None podle_data = btc.generate_podle(priv_utxo_pairs, tries, ext_valid) if podle_data: log.debug("Generated PoDLE: " + pprint.pformat(podle_data)) revelation = btc.PoDLE(u=podle_data['utxo'], P=podle_data['P'], P2=podle_data['P2'], s=podle_data['sig'], e=podle_data['e']).serialize_revelation() return (commit_type_byte + podle_data["commit"], revelation) else: #we know that priv_utxo_pairs all passed age and size tests, so #they must have failed the retries test. Summarize this info #and publish to commitments_debug.txt with open("commitments_debug.txt", "wb") as f: f.write("THIS IS A TEMPORARY FILE FOR DEBUGGING; " "IT CAN BE SAFELY DELETED ANY TIME.\n") f.write("***\n") f.write("1: Utxos that passed age and size limits, but have " "been used too many times (see taker_utxo_retries " "in the config):\n") if len(priv_utxo_pairs) == 0: f.write("None\n") else: for p, u in priv_utxo_pairs: f.write(str(u) + "\n") f.write("2: Utxos that have less than " + jm_single().config.get("POLICY", "taker_utxo_age") + " confirmations:\n") if len(to) == 0: f.write("None\n") else: for t in to: f.write(str(t) + "\n") f.write("3: Utxos that were not at least " + \ jm_single().config.get( "POLICY", "taker_utxo_amtpercent") + "% of the " "size of the coinjoin amount " + str( self.proposed_cj_amount) + "\n") if len(ts) == 0: f.write("None\n") else: for t in ts: f.write(str(t) + "\n") f.write('***\n') f.write( "Utxos that appeared in item 1 cannot be used again.\n") f.write( "Utxos only in item 2 can be used by waiting for more " "confirmations, (set by the value of taker_utxo_age).\n") f.write("Utxos only in item 3 are not big enough for this " "coinjoin transaction, set by the value " "of taker_utxo_amtpercent.\n") f.write( "If you cannot source a utxo from your wallet according " "to these rules, use the tool add-utxo.py to source a " "utxo external to your joinmarket wallet. Read the help " "with 'python add-utxo.py --help'\n\n") f.write("You can also reset the rules in the joinmarket.cfg " "file, but this is generally inadvisable.\n") f.write( "***\nFor reference, here are the utxos in your wallet:\n") f.write("\n" + str(self.proposed_wallet.unspent)) return (None, None)
def make_commitment(self, wallet, input_utxos, cjamount): """The Taker default commitment function, which uses PoDLE. Alternative commitment types should use a different commit type byte. This will allow future upgrades to provide different style commitments by subclassing Taker and changing the commit_type_byte; existing makers will simply not accept this new type of commitment. In case of success, return the commitment and its opening. In case of failure returns (None, None) and constructs a detailed log for the user to read and discern the reason. """ def filter_by_coin_age_amt(utxos, age, amt): results = jm_single().bc_interface.query_utxo_set(utxos, includeconf=True) newresults = [] too_old = [] too_small = [] for i, r in enumerate(results): #results return "None" if txo is spent; drop this if not r: continue valid_age = r['confirms'] >= age valid_amt = r['value'] >= amt if not valid_age: too_old.append(utxos[i]) if not valid_amt: too_small.append(utxos[i]) if valid_age and valid_amt: newresults.append(utxos[i]) return newresults, too_old, too_small def priv_utxo_pairs_from_utxos(utxos, age, amt): #returns pairs list of (priv, utxo) for each valid utxo; #also returns lists "too_old" and "too_small" for any #utxos that did not satisfy the criteria for debugging. priv_utxo_pairs = [] new_utxos, too_old, too_small = filter_by_coin_age_amt( utxos.keys(), age, amt) new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos} for k, v in new_utxos_dict.iteritems(): addr = v['address'] priv = wallet.get_key_from_addr(addr) if priv: #can be null from create-unsigned priv_utxo_pairs.append((priv, k)) return priv_utxo_pairs, too_old, too_small commit_type_byte = "P" podle_data = None tries = jm_single().config.getint("POLICY", "taker_utxo_retries") age = jm_single().config.getint("POLICY", "taker_utxo_age") #Minor rounding errors don't matter here amt = int(cjamount * jm_single().config.getint( "POLICY", "taker_utxo_amtpercent") / 100.0) priv_utxo_pairs, to, ts = priv_utxo_pairs_from_utxos(input_utxos, age, amt) #Note that we ignore the "too old" and "too small" lists in the first #pass through, because the same utxos appear in the whole-wallet check. #For podle data format see: btc.podle.PoDLE.reveal() #In first round try, don't use external commitments podle_data = btc.generate_podle(priv_utxo_pairs, tries) if not podle_data: #We defer to a second round to try *all* utxos in wallet; #this is because it's much cleaner to use the utxos involved #in the transaction, about to be consumed, rather than use #random utxos that will persist after. At this step we also #allow use of external utxos in the json file. if wallet.unspent: priv_utxo_pairs, to, ts = priv_utxo_pairs_from_utxos( wallet.unspent, age, amt) #Pre-filter the set of external commitments that work for this #transaction according to its size and age. dummy, extdict = btc.get_podle_commitments() if len(extdict.keys()) > 0: ext_valid, ext_to, ext_ts = filter_by_coin_age_amt(extdict.keys(), age, amt) else: ext_valid = None podle_data = btc.generate_podle(priv_utxo_pairs, tries, ext_valid) if podle_data: log.debug("Generated PoDLE: " + pprint.pformat(podle_data)) revelation = btc.PoDLE(u=podle_data['utxo'],P=podle_data['P'], P2=podle_data['P2'],s=podle_data['sig'], e=podle_data['e']).serialize_revelation() return (commit_type_byte + podle_data["commit"], revelation) else: #we know that priv_utxo_pairs all passed age and size tests, so #they must have failed the retries test. Summarize this info #and publish to commitments_debug.txt with open("commitments_debug.txt", "wb") as f: f.write("THIS IS A TEMPORARY FILE FOR DEBUGGING; " "IT CAN BE SAFELY DELETED ANY TIME.\n") f.write("***\n") f.write("1: Utxos that passed age and size limits, but have " "been used too many times (see taker_utxo_retries " "in the config):\n") if len(priv_utxo_pairs) == 0: f.write("None\n") else: for p, u in priv_utxo_pairs: f.write(str(u) + "\n") f.write("2: Utxos that have less than " + jm_single().config.get( "POLICY", "taker_utxo_age") + " confirmations:\n") if len(to) == 0: f.write("None\n") else: for t in to: f.write(str(t) + "\n") f.write("3: Utxos that were not at least " + \ jm_single().config.get( "POLICY", "taker_utxo_amtpercent") + "% of the " "size of the coinjoin amount " + str( self.proposed_cj_amount) + "\n") if len(ts) == 0: f.write("None\n") else: for t in ts: f.write(str(t) + "\n") f.write('***\n') f.write("Utxos that appeared in item 1 cannot be used again.\n") f.write("Utxos only in item 2 can be used by waiting for more " "confirmations, (set by the value of taker_utxo_age).\n") f.write("Utxos only in item 3 are not big enough for this " "coinjoin transaction, set by the value " "of taker_utxo_amtpercent.\n") f.write("If you cannot source a utxo from your wallet according " "to these rules, use the tool add-utxo.py to source a " "utxo external to your joinmarket wallet. Read the help " "with 'python add-utxo.py --help'\n\n") f.write("You can also reset the rules in the joinmarket.cfg " "file, but this is generally inadvisable.\n") f.write("***\nFor reference, here are the utxos in your wallet:\n") f.write("\n" + str(self.proposed_wallet.unspent)) return (None, None)