def test_auth_pub_not_found(setup_taker): taker = get_taker([(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw", 0, NO_ROUNDING)]) orderbook = copy.deepcopy(t_orderbook) res = taker.initialize(orderbook, []) taker.orderbook = copy.deepcopy( t_chosen_orders) #total_cjfee unaffected, all same maker_response = copy.deepcopy(t_maker_response) utxos = [ utxostr_to_utxo(x)[1] for x in [ "03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6:1", "498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3:0", "3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c:1" ] ] fake_query_results = [{ 'value': 200000000, 'address': "mrKTGvFfYUEqk52qPKUroumZJcpjHLQ6pn", 'script': hextobin('76a914767c956efe6092a775fea39a06d1cac9aae956d788ac'), 'utxo': utxos[i], 'confirms': 20 } for i in range(3)] jm_single().bc_interface.insert_fake_query_results(fake_query_results) res = taker.receive_utxos(maker_response) assert not res[0] assert res[1] == "Not enough counterparties responded to fill, giving up" jm_single().bc_interface.insert_fake_query_results(None)
def get_utxo_info(upriv, utxo_binary=False): """Verify that the input string parses correctly as (utxo, priv) and return that. If `utxo_binary` is true, the first element of that return tuple is the standard internal form (txid-in-binary, index-as-int). """ try: u, priv = upriv.split(',') u = u.strip() priv = priv.strip() success, utxo_bin = utxostr_to_utxo(u) assert success, utxo except: #not sending data to stdout in case privkey info jmprint("Failed to parse utxo information for utxo", "error") raise try: # see note below for why keytype is ignored, and note that # this calls read_privkey to validate. raw, _ = BTCEngine.wif_to_privkey(priv) except: jmprint("failed to parse privkey, make sure it's WIF compressed format.", "error") raise utxo_to_return = utxo_bin if utxo_binary else u return utxo_to_return, priv
def test_sign_external_psbt(setup_psbt_wallet, walletseed, xpub, spktype_wallet, spktype_destn, partial, psbt): bitcoin.select_chain_params("bitcoin") wallet_cls = SegwitWallet if spktype_wallet == "p2wpkh" else SegwitLegacyWallet wallet = create_volatile_wallet(walletseed, wallet_cls=wallet_cls) # if we want to actually sign, our wallet has to recognize the fake utxos # as being in the wallet, so we inject them: class DummyUtxoManager(object): _utxo = {0: {}} def add_utxo(self, utxo, path, value, height): self._utxo[0][utxo] = (path, value, height) wallet._index_cache[0][0] = 1000 wallet._utxos = DummyUtxoManager() p0, p1, p2 = (wallet.get_path(0, 0, i) for i in range(3)) if not partial: wallet._utxos.add_utxo( utxostr_to_utxo( "0b7468282e0c5fd82ee6b006ed5057199a7b3a1c4422e58ddfb35c5e269684bb:0" ), p0, 10000, 1) wallet._utxos.add_utxo( utxostr_to_utxo( "442d551b314efd28f49c89e06b9495efd0fbc8c64fd06f398a73ad47c6447df9:0" ), p1, 10000, 1) wallet._utxos.add_utxo( utxostr_to_utxo( "4bda19e193781fb899511a052717fa38cd4d341a4f6dc29b6cb10c854c29e76b:0" ), p2, 10000, 1) signresult_and_signedpsbt, err = wallet.sign_psbt(base64.b64decode( psbt.encode("ascii")), with_sign_result=True) assert not err signresult, signedpsbt = signresult_and_signedpsbt if partial: assert not signresult.is_final assert signresult.num_inputs_signed == 2 assert signresult.num_inputs_final == 2 else: assert signresult.is_final assert signresult.num_inputs_signed == 3 assert signresult.num_inputs_final == 3 print(PSBTWalletMixin.human_readable_psbt(signedpsbt)) bitcoin.select_chain_params("bitcoin/regtest")
def test_get_utxo_info(): load_test_config() # this test tests mainnet keys, so temporarily switch network select_chain_params("bitcoin") jm_single().config.set("BLOCKCHAIN", "network", "mainnet") dbci = DummyBlockchainInterface() privkey = "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi" #to verify use from_wif_privkey and privkey_to_address iaddr = "bc1q6tvmnmetj8vfz98vuetpvtuplqtj4uvvwjgxxc" fakeutxo = "aa" * 32 + ":08" success, fakeutxo_bin = utxostr_to_utxo(fakeutxo) assert success fake_query_results = [{ 'value': 200000000, 'script': BTC_P2WPKH.address_to_script(iaddr), 'utxo': fakeutxo_bin, 'confirms': 20 }] dbci.insert_fake_query_results(fake_query_results) jm_single().bc_interface = dbci u, priv = get_utxo_info(fakeutxo + "," + privkey) assert u == fakeutxo assert priv == privkey #invalid format with pytest.raises(Exception): u, priv = get_utxo_info(fakeutxo + privkey) #invalid index fu2 = "ab" * 32 + ":-1" with pytest.raises(Exception): u, priv = get_utxo_info(fu2 + "," + privkey) #invalid privkey p2 = privkey[:-1] + 'j' with pytest.raises(Exception): u, priv = get_utxo_info(fakeutxo + "," + p2) utxodatas = [(fakeutxo_bin, privkey)] retval = validate_utxo_data(utxodatas, False) assert retval #try to retrieve retval = validate_utxo_data(utxodatas, True) assert retval[0] == (fakeutxo_bin, 200000000) fake_query_results[0]['script'] = hextobin( "76a91479b000887626b294a914501a4cd226b58b23598388ac") dbci.insert_fake_query_results(fake_query_results) #validate should fail for wrong address retval = validate_utxo_data(utxodatas, False) assert not retval #remove fake query result and trigger not found dbci.fake_query_results = None dbci.setQUSFail(True) retval = validate_utxo_data(utxodatas, False) assert not retval dbci.setQUSFail(False) select_chain_params("bitcoin/regtest") jm_single().config.set("BLOCKCHAIN", "network", "regtest")
def get_utxos_by_mixdepth(self, include_disabled=False, verbose=True, includeheight=False): # utxostr conversion routines because taker_test_data uses hex: retval = {} for mixdepth, v in t_utxos_by_mixdepth.items(): retval[mixdepth] = {} for i, (utxo, val) in enumerate(v.items()): retval[mixdepth][utxostr_to_utxo(utxo)[1]] = val val["script"] = self._ENGINE.address_to_script(val['address']) val["path"] = (b'dummy', mixdepth, i) return retval
def main(): parser = OptionParser( usage='usage: %prog [options] utxo destaddr1 destaddr2 ..', description=description, formatter=IndentedHelpFormatterWithNL()) parser.add_option( '-t', '--utxo-address-type', action='store', dest='utxo_address_type', help= ('type of address of coin being spent - one of "p2pkh", "p2wpkh", "p2sh-p2wpkh". ' 'No other scriptpubkey types (e.g. multisig) are supported. If not set, we default ' 'to what is in joinmarket.cfg.'), default="") add_base_options(parser) (options, args) = parser.parse_args() load_program_config(config_path=options.datadir) if len(args) < 2: quit(parser, 'Invalid syntax') u = args[0] priv = input('input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: quit(parser, "Failed to parse utxo info: " + u) destaddrs = args[1:] for d in destaddrs: if not validate_address(d): quit(parser, "Address was not valid; wrong network?: " + d) success, utxo = utxostr_to_utxo(u) if not success: quit(parser, "Failed to load utxo from string: " + utxo) if options.utxo_address_type == "": if jm_single().config.get("POLICY", "segwit") == "false": utxo_address_type = "p2pkh" elif jm_single().config.get("POLICY", "native") == "false": utxo_address_type = "p2sh-p2wpkh" else: utxo_address_type = "p2wpkh" else: utxo_address_type = options.utxo_address_type txsigned = sign(utxo, priv, destaddrs, utxo_address_type) if not txsigned: log.info( "Transaction signing operation failed, see debug messages for details." ) return log.info("Got signed transaction:\n" + bintohex(txsigned.serialize())) log.info(btc.human_readable_transaction(txsigned)) if input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(txsigned.serialize())
def select_utxos(self, mixdepth, amount, utxo_filter=None, select_fn=None, maxheight=None, includeaddr=False): if amount > self.get_balance_by_mixdepth()[mixdepth]: raise NotEnoughFundsException(amount, self.get_balance_by_mixdepth()[mixdepth]) # comment as for get_utxos_by_mixdepth: retval = {} for k, v in t_utxos_by_mixdepth[mixdepth].items(): success, u = utxostr_to_utxo(k) assert success retval[u] = v retval[u]["script"] = self.addr_to_script(retval[u]["address"]) return retval
def deserialize_revelation(cls, ser_rev, separator='|'): """ Reads the over-the-wire format as used in Joinmarket communication protocol. """ ser_list = ser_rev.split(separator) if len(ser_list) != 5: raise PoDLEError("Failed to deserialize, wrong format") utxostr, P, P2, s, e = ser_list success, utxo = utxostr_to_utxo(utxostr) assert success, "invalid utxo format in PoDLE." return {'utxo': utxo, 'P': hextobin(P), 'P2': hextobin(P2), 'sig': hextobin(s), 'e': hextobin(e)}
def convert_utxos(utxodict): return_dict = {} for uk, val in utxodict.items(): return_dict[utxostr_to_utxo(uk)[1]] = val return return_dict
def main(): parser = OptionParser( usage= 'usage: %prog [options] utxo destaddr1 destaddr2 ..', description="For creating multiple utxos from one (for commitments in JM)." "Provide a utxo in form txid:N that has some unspent coins;" "Specify a list of destination addresses and the coins will" "be split equally between them (after bitcoin fees)." "You'll be prompted to enter the private key for the utxo" "during the run; it must be in WIF compressed format." "After the transaction is completed, the utxo strings for" "the new outputs will be shown." "Note that these utxos will not be ready for use as external" "commitments in Joinmarket until 5 confirmations have passed." " BE CAREFUL about handling private keys!" " Don't do this in insecure environments." " Works only with p2pkh ('1') or p2sh-p2wpkh (segwit '3')" " utxos - set segwit=False in the POLICY section of" " joinmarket.cfg for the former." ) parser.add_option( '-t', '--utxo-address-type', action='store', dest='utxo_address_type', help=('type of address of coin being spent - one of "p2pkh", "p2wpkh", "p2sh-p2wpkh". ' 'No other scriptpubkey types (e.g. multisig) are supported. If not set, we default ' 'to what is in joinmarket.cfg.'), default="" ) add_base_options(parser) (options, args) = parser.parse_args() load_program_config(config_path=options.datadir) if len(args) < 2: quit(parser, 'Invalid syntax') u = args[0] priv = input( 'input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: quit(parser, "Failed to parse utxo info: " + u) destaddrs = args[1:] for d in destaddrs: if not validate_address(d): quit(parser, "Address was not valid; wrong network?: " + d) success, utxo = utxostr_to_utxo(u) if not success: quit(parser, "Failed to load utxo from string: " + utxo) if options.utxo_address_type == "": if jm_single().config.get("POLICY", "segwit") == "false": utxo_address_type = "p2pkh" elif jm_single().config.get("POLICY", "native") == "false": utxo_address_type = "p2sh-p2wpkh" else: utxo_address_type = "p2wpkh" else: utxo_address_type = options.utxo_address_type txsigned = sign(utxo, priv, destaddrs, utxo_address_type) if not txsigned: log.info("Transaction signing operation failed, see debug messages for details.") return log.info("Got signed transaction:\n" + bintohex(txsigned.serialize())) log.info(btc.human_readable_transaction(txsigned)) if input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(txsigned.serialize())
def main() -> None: parser = OptionParser( usage="usage: %prog [options] UTXO or amount", description=DESCRIPTION, ) add_base_options(parser) parser.add_option( "-i", "--interest", action="store", type="float", dest="interest", help= "Interest rate to use for fidelity bond calculation (instead of interest_rate config)", ) parser.add_option( "-e", "--exponent", action="store", type="float", dest="exponent", help= "Exponent to use for fidelity bond calculation (instead of bond_value_exponent config)", ) parser.add_option( "-m", "--months", action="store", type="int", dest="months", help= "For how many months to calculate the fidelity bond values, each month has its own stats (default 12)", default=12, ) parser.add_option( "-o", "--orderbook", action="store", type="str", dest="path_to_json", help="Path to the exported orderbook in JSON format", ) options, args = parser.parse_args() load_program_config(config_path=options.datadir) if len(args) != 1: log.error("Invalid arguments, see --help") sys.exit(EXIT_ARGERROR) if options.path_to_json: try: with open(options.path_to_json, "r", encoding="UTF-8") as orderbook: orderbook = loads(orderbook.read()) except FileNotFoundError as exc: log.error(exc) sys.exit(EXIT_ARGERROR) else: orderbook = None try: amount = amount_to_sat(args[0]) confirm_time = None except ValueError: # If it's not a valid amount then it has to be a UTXO if jm_single().bc_interface is None: log.error( "For calculation based on UTXO access to Bitcoin Core is required" ) sys.exit(EXIT_FAILURE) success, utxo = utxostr_to_utxo(args[0]) if not success: # utxo contains the error message log.error(utxo) sys.exit(EXIT_ARGERROR) utxo_data = jm_single().bc_interface.query_utxo_set( utxo, includeconfs=True)[0] amount = utxo_data["value"] if utxo_data["confirms"] == 0: log.warning( "Given UTXO is unconfirmed, current time will be used as confirmation time" ) confirm_time = None elif utxo_data["confirms"] < 0: log.error("Given UTXO is invalid, reason: conflicted") sys.exit(EXIT_ARGERROR) else: current_height = jm_single().bc_interface.get_current_block_height( ) block_hash = jm_single().bc_interface.get_block_hash( current_height - utxo_data["confirms"] + 1) confirm_time = jm_single().bc_interface.get_block_time(block_hash) parameters, results = get_bond_values(amount, options.months, confirm_time, options.interest, options.exponent, orderbook) jmprint(f"Amount locked: {amount} ({sat_to_btc(amount)} btc)") jmprint( f"Confirmation time: {datetime.fromtimestamp(parameters['confirm_time'])}" ) jmprint( f"Interest rate: {parameters['interest']} ({parameters['interest'] * 100}%)" ) jmprint(f"Exponent: {parameters['exponent']}") jmprint(f"\nFIDELITY BOND VALUES (BTC^{parameters['exponent']})") jmprint("\nSee /docs/fidelity-bonds.md for complete formula and more") for result in results: locktime = datetime.fromtimestamp(result["locktime"]) # Mimic the locktime value the user would have to insert to create such fidelity bond jmprint(f"\nLocktime: {locktime.year}-{locktime.month}") # Mimic orderbook value jmprint( f"Bond value: {float(Decimal(result['value']) / Decimal(1e16)):.16f}" ) if options.path_to_json: jmprint( f"Weight: {result['weight']:.5f} ({result['weight'] * 100:.2f}% of all bonds)" ) jmprint(f"Top {result['percentile']}% of the orderbook by value")
def main(): parser = OptionParser( usage='usage: %prog [options] utxo destaddr1 destaddr2 ..', description= "For creating multiple utxos from one (for commitments in JM)." "Provide a utxo in form txid:N that has some unspent coins;" "Specify a list of destination addresses and the coins will" "be split equally between them (after bitcoin fees)." "You'll be prompted to enter the private key for the utxo" "during the run; it must be in WIF compressed format." "After the transaction is completed, the utxo strings for" "the new outputs will be shown." "Note that these utxos will not be ready for use as external" "commitments in Joinmarket until 5 confirmations have passed." " BE CAREFUL about handling private keys!" " Don't do this in insecure environments." " Works only with p2pkh ('1') or p2sh-p2wpkh (segwit '3')" " utxos - set segwit=False in the POLICY section of" " joinmarket.cfg for the former.") parser.add_option( '-v', '--validate-utxos', action='store_true', dest='validate', help='validate the utxos and pubkeys provided against the blockchain', default=False) parser.add_option( '-o', '--validate-only', action='store_true', dest='vonly', help='only validate the provided utxos (file or command line), not add', default=False) parser.add_option( '-n', '--non-segwit-input', action='store_true', dest='nonsegwit', help= 'input is p2pkh ("1" address), not segwit; if not used, input is assumed to be segwit type.', default=False) add_base_options(parser) (options, args) = parser.parse_args() load_program_config(config_path=options.datadir) if len(args) < 2: quit(parser, 'Invalid syntax') u = args[0] priv = input('input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: quit(parser, "Failed to parse utxo info: " + u) destaddrs = args[1:] for d in destaddrs: if not validate_address(d): quit(parser, "Address was not valid; wrong network?: " + d) success, utxo = utxostr_to_utxo(u) if not success: quit(parser, "Failed to load utxo from string: " + utxo) txsigned = sign(utxo, priv, destaddrs, segwit=not options.nonsegwit) if not txsigned: log.info( "Transaction signing operation failed, see debug messages for details." ) return log.info("Got signed transaction:\n" + bintohex(txsigned.serialize())) log.debug("Deserialized:") log.debug(pformat(str(txsigned))) if input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(txsigned.serialize())