def test_imported_privkey(setup_wallets): for n in ["mainnet", "testnet"]: privkey = "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb401" jm_single().config.set("BLOCKCHAIN", "network", n) password = "******" password_key = bitcoin.bin_dbl_sha256(password) wifprivkey = bitcoin.wif_compressed_privkey(privkey, get_p2pk_vbyte()) #mainnet is "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi" #to verify use from_wif_privkey and privkey_to_address if n == "mainnet": iaddr = "1LDsjB43N2NAQ1Vbc2xyHca4iBBciN8iwC" else: iaddr = "mzjq2E92B3oRB7yDKbwM7XnPaAnKfRERw2" privkey_bin = bitcoin.from_wif_privkey( wifprivkey, vbyte=get_p2pk_vbyte()).decode('hex')[:-1] encrypted_privkey = encryptData(password_key, privkey_bin) encrypted_privkey_bad = encryptData(password_key, privkey_bin[:6]) walletdir = "wallets" testwalletname = "test" + n pathtowallet = os.path.join(walletdir, testwalletname) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") for ep in [encrypted_privkey, encrypted_privkey_bad]: walletfilejson = { 'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': n, 'index_cache': [[0, 0]] * 5, 'imported_keys': [{ 'encrypted_privkey': ep.encode('hex'), 'mixdepth': 0 }] } walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if ep == encrypted_privkey_bad: with pytest.raises(Exception) as e_info: Wallet(testwalletname, password, 5, 6, False, False) continue newwallet = Wallet(testwalletname, password, 5, 6, False, False) assert newwallet.seed == seed #test accessing the key from the addr assert newwallet.get_key_from_addr( iaddr) == bitcoin.from_wif_privkey(wifprivkey, vbyte=get_p2pk_vbyte()) if n == "testnet": jm_single().bc_interface.sync_wallet(newwallet) load_program_config()
def make_wallets(n, wallet_structures=None, mean_amt=1, sdev_amt=0, start_index=0, fixed_seeds=None, test_wallet=False, passwords=None): '''n: number of wallets to be created wallet_structure: array of n arrays , each subarray specifying the number of addresses to be populated with coins at each depth (for now, this will only populate coins into 'receive' addresses) mean_amt: the number of coins (in btc units) in each address as above sdev_amt: if randomness in amouts is desired, specify here. Returns: a dict of dicts of form {0:{'seed':seed,'wallet':Wallet object},1:..,} Default Wallet constructor is joinmarket.Wallet, else use TestWallet, which takes a password parameter as in the list passwords. ''' if len(wallet_structures) != n: raise Exception("Number of wallets doesn't match wallet structures") if not fixed_seeds: seeds = chunks(binascii.hexlify(os.urandom(15 * n)), 15 * 2) else: seeds = fixed_seeds wallets = {} for i in range(n): if test_wallet: w = Wallet(seeds[i], passwords[i], max_mix_depth=5) else: w = Wallet(seeds[i], None, max_mix_depth=5) wallets[i + start_index] = {'seed': seeds[i], 'wallet': w} for j in range(5): for k in range(wallet_structures[i][j]): deviation = sdev_amt * random.random() amt = mean_amt - sdev_amt / 2.0 + deviation if amt < 0: amt = 0.001 amt = float(Decimal(amt).quantize(Decimal(10)**-8)) jm_single().bc_interface.grab_coins( wallets[i + start_index]['wallet'].get_external_addr(j), amt) #reset the index so the coins can be seen if running in same script wallets[ i + start_index]['wallet'].index[j][0] -= wallet_structures[i][j] return wallets
def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method): jm_single().config.set("BLOCKCHAIN", "network", net) wallet = Wallet(seed, None, max_mix_depth=5) sync_wallet(wallet) #copy pasted from wallet-tool; some boiled down form of #this should really be in wallet.py in the joinmarket module. def cus_print(s): print s total_balance = 0 for m in range(wallet.max_mix_depth): cus_print('mixing depth %d m/0/%d/' % (m, m)) balance_depth = 0 for forchange in [0, 1]: cus_print(' ' + ('external' if forchange == 0 else 'internal') + ' addresses m/0/%d/%d/' % (m, forchange)) for k in range(wallet.index[m][forchange] + gaplimit): addr = wallet.get_addr(m, forchange, k) balance = 0.0 for addrvalue in wallet.unspent.values(): if addr == addrvalue['address']: balance += addrvalue['value'] balance_depth += balance used = ('used' if k < wallet.index[m][forchange] else ' new') if showprivkey: privkey = btc.wif_compressed_privkey( wallet.get_key(m, forchange, k), get_p2pk_vbyte()) else: privkey = '' if (method == 'displayall' or balance > 0 or (used == ' new' and forchange == 0)): cus_print( ' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % (m, forchange, k, addr, used, balance / 1e8, privkey)) total_balance += balance_depth print('for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8)) assert total_balance == 96085297
def create_default_testnet_wallet(): walletdir = "wallets" testwalletname = "testwallet.json" pathtowallet = os.path.join(walletdir, testwalletname) if os.path.exists(pathtowallet): os.remove(pathtowallet) seed = "hello" return (walletdir, pathtowallet, testwalletname, Wallet(seed, None, 5, 6, extend_mixdepth=False, storepassword=False))
def main(): parser = OptionParser( usage='usage: %prog [options] [txid:n]', description= "Adds one or more utxos to the list that can be used to make " "commitments for anti-snooping. Note that this utxo, and its " "PUBkey, will be revealed to makers, so consider the privacy " "implication. " "It may be useful to those who are having trouble making " "coinjoins due to several unsuccessful attempts (especially " "if your joinmarket wallet is new). " "'Utxo' means unspent transaction output, it must not " "already be spent. " "The options -w, -r and -R offer ways to load these utxos " "from a file or wallet. " "If you enter a single utxo without these options, you will be " "prompted to enter the private key here - it must be in " "WIF compressed format. " "BE CAREFUL about handling private keys! " "Don't do this in insecure environments. " "Also note this ONLY works for standard (p2pkh) utxos.") parser.add_option( '-r', '--read-from-file', action='store', type='str', dest='in_file', help= 'name of plain text csv file containing utxos, one per line, format: ' 'txid:N, WIF-compressed-privkey') parser.add_option( '-R', '--read-from-json', action='store', type='str', dest='in_json', help= 'name of json formatted file containing utxos with private keys, as ' 'output from "python wallet-tool.py -u -p walletname showutxos"') parser.add_option( '-w', '--load-wallet', action='store', type='str', dest='loadwallet', help='name of wallet from which to load utxos and use as commitments.') parser.add_option( '-g', '--gap-limit', action='store', type='int', dest='gaplimit', default=6, help= 'Only to be used with -w; gap limit for Joinmarket wallet, default 6.') parser.add_option( '-M', '--max-mixdepth', action='store', type='int', dest='maxmixdepth', default=5, help= 'Only to be used with -w; number of mixdepths for wallet, default 5.') parser.add_option( '-d', '--delete-external', action='store_true', dest='delete_ext', help='deletes the current list of external commitment utxos', default=False) 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('--fast', action='store_true', dest='fastsync', default=False, help=('choose to do fast wallet sync, only for Core and ' 'only for previously synced wallet')) (options, args) = parser.parse_args() load_program_config() #TODO; sort out "commit file location" global so this script can #run without this hardcoding: utxo_data = [] if options.delete_ext: other = options.in_file or options.in_json or options.loadwallet if len(args) > 0 or other: if raw_input( "You have chosen to delete commitments, other arguments " "will be ignored; continue? (y/n)") != 'y': print "Quitting" sys.exit(0) c, e = get_podle_commitments() print pformat(e) if raw_input( "You will remove the above commitments; are you sure? (y/n): " ) != 'y': print "Quitting" sys.exit(0) update_commitments(external_to_remove=e) print "Commitments deleted." sys.exit(0) #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, #csv file or json file. if options.loadwallet: while True: pwd = get_password("Enter wallet decryption passphrase: ") try: wallet = Wallet(options.loadwallet, pwd, options.maxmixdepth, options.gaplimit) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e)) sys.exit(0) break sync_wallet(wallet, fast=options.fastsync) unsp = {} for u, av in wallet.unspent.iteritems(): addr = av['address'] key = wallet.get_key_from_addr(addr) wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2pk_vbyte()) unsp[u] = { 'address': av['address'], 'value': av['value'], 'privkey': wifkey } for u, pva in unsp.iteritems(): utxo_data.append((u, pva['privkey'])) elif options.in_file: with open(options.in_file, "rb") as f: utxo_info = f.readlines() for ul in utxo_info: ul = ul.rstrip() if ul: u, priv = get_utxo_info(ul) if not u: quit(parser, "Failed to parse utxo info: " + str(ul)) utxo_data.append((u, priv)) elif options.in_json: if not os.path.isfile(options.in_json): print "File: " + options.in_json + " not found." sys.exit(0) with open(options.in_json, "rb") as f: try: utxo_json = json.loads(f.read()) except: print "Failed to read json from " + options.in_json sys.exit(0) for u, pva in utxo_json.iteritems(): utxo_data.append((u, pva['privkey'])) elif len(args) == 1: u = args[0] priv = raw_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) utxo_data.append((u, priv)) else: quit(parser, 'Invalid syntax') if options.validate or options.vonly: if not validate_utxo_data(utxo_data): quit(parser, "Utxos did not validate, quitting") if options.vonly: sys.exit(0) #We are adding utxos to the external list assert len(utxo_data) add_ext_commitments(utxo_data)
def main_cs(test_data=None): #twisted logging (TODO disable for non-debug runs) if test_data: wallet_name, args, options, use_ssl, alt_class, alt_c_class = test_data else: log.startLogging(sys.stdout) #Joinmarket wallet parser = get_coinswap_parser() (options, args) = parser.parse_args() load_coinswap_config() wallet_name = args[0] #depth 0: spend in, depth 1: receive out, depth 2: for backout transactions. max_mix_depth = 3 if not os.path.exists(os.path.join('wallets', wallet_name)): wallet = Wallet(wallet_name, None, max_mix_depth, 6) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") wallet = Wallet(wallet_name, pwd, max_mix_depth, 6) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e)) sys.exit(0) break #for testing main script (not test framework), need funds. if not test_data and isinstance(cs_single().bc_interface, RegtestBitcoinCoreInterface): cs_single().bc_interface.grab_coins(wallet.get_new_addr(0, 0), 2.0) time.sleep(3) sync_wallet(wallet, fast=options.fastsync) wallet.used_coins = None if options.serve: #sanity check that client params were not provided: if len(args) > 1: print("Extra parameters provided for running as server. " "Are you sure you didn't want to run as client?") sys.exit(0) if not test_data: main_server(options, wallet) else: main_server(options, wallet, { 'use_ssl': use_ssl, 'alt_c_class': alt_c_class }) return wallet.get_balance_by_mixdepth() return tx01_amount = int(args[1]) #Reset the targetting for backout transactions oldtarget = cs_single().config.get("POLICY", "tx_fees") newtarget = cs_single().config.getint("POLICY", "backout_fee_target") multiplier = float(cs_single().config.get("POLICY", "backout_fee_multiplier")) cs_single().config.set("POLICY", "tx_fees", str(newtarget)) tx23fee = estimate_tx_fee((1, 2, 2), 1, txtype='p2shMofN') tx23fee = int(multiplier * tx23fee) tx24_recipient_amount = tx01_amount - tx23fee tx35_recipient_amount = tx01_amount - tx23fee cs_single().config.set("POLICY", "tx_fees", oldtarget) #to allow testing of confirm/unconfirm callback for multiple txs if isinstance(cs_single().bc_interface, RegtestBitcoinCoreInterface): cs_single().bc_interface.tick_forward_chain_interval = 2 cs_single().bc_interface.simulating = True cs_single().config.set("BLOCKCHAIN", "notify_port", "62652") cs_single().config.set("BLOCKCHAIN", "rpc_host", "127.0.0.2") #if restart option selected, read state and backout if options.recover: session_id = options.recover alice = CoinSwapAlice(wallet, 'alicestate') alice.bbmb = wallet.get_balance_by_mixdepth(verbose=False) alice.load(sessionid=session_id) alice.backout("Recovering from shutdown") reactor.run() return if len(args) > 2: tx5address = args[2] if not validate_address(tx5address): print("Invalid address: ", tx5address) sys.exit(0) else: #Our destination address should be in a separate mixdepth tx5address = wallet.get_new_addr(1, 1) #instantiate the parameters, but don't yet have the ephemeral pubkeys #or destination addresses. cpp = CoinSwapPublicParameters(tx01_amount, tx24_recipient_amount, tx35_recipient_amount) #Alice must set the unique identifier for this run. cpp.set_session_id() cpp.set_tx5_address(tx5address) testing_mode = True if test_data else False aliceclass = alt_class if test_data and alt_class else CoinSwapAlice alice = aliceclass(wallet, 'alicestate', cpp, testing_mode=testing_mode) scheme, server, port = options.serverport.split(":") print("got this scheme, server, port: ", scheme, server, port) if scheme == "https": usessl = True elif scheme == "http": usessl = False else: print("Invalid server string: ", options.serverport) sys.exit(0) if not server[:2] == "//": print("Invalid server string: ", options.serverport) alice_client = CoinSwapJSONRPCClient(server[2:], port, alice.sm.tick, alice.backout, usessl) alice.set_jsonrpc_client(alice_client) reactor.callWhenRunning(alice.sm.tick) if not test_data: reactor.run() if test_data: return alice
def test_wallet_create(setup_wallets, includecache, wrongnet, storepwd, extendmd, pwdnumtries): walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet( ) assert wallet.get_key( 4, 1, 17) == "1289ca322f96673acef83f396a9735840e3ab69f0459cf9bfa8d9985a876534401" assert wallet.get_addr(2, 0, 5) == "myWPu9QJWHGE79XAmuKkwKgNk8vsr5evpk" jm_single().bc_interface.wallet_synced = True assert wallet.get_new_addr(1, 0) == "mi88ZgDGPmarzcsU6S437h9CY9BLmgH5M6" assert wallet.get_external_addr(3) == "mvChQuChnXVhqvH67wfMxrodPQ7xccdVJU" addr3internal = wallet.get_internal_addr(3) assert addr3internal == "mv26o79Bauf2miJMoxoSu1vXmfXnk85YPQ" assert wallet.get_key_from_addr( addr3internal) == "2a283c9a2168a25509e2fb944939637228c50c8b4fecd9024650316c4584246501" dummyaddr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU" assert not wallet.get_key_from_addr(dummyaddr) #Make a new Wallet(), and prepare a testnet wallet file for this wallet password = "******" password_key = bitcoin.bin_dbl_sha256(password) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") net = get_network() if not wrongnet else 'mainnnet' walletfilejson = {'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': net} if includecache: mmd = wallet.max_mix_depth if not extendmd else wallet.max_mix_depth + 5 print("using mmd: " + str(mmd)) walletfilejson.update({'index_cache': [[0, 0]] * mmd}) walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if wrongnet: with pytest.raises(ValueError) as e_info: Wallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) return from string import ascii_letters for i in range( pwdnumtries): #multiple tries to ensure pkcs7 error is triggered with pytest.raises(WalletError) as e_info: wrongpwd = "".join([random.choice(ascii_letters) for _ in range(20) ]) Wallet(testwalletname, wrongpwd, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) with pytest.raises(WalletError) as e_info: Wallet(testwalletname, None, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) newwallet = Wallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) assert newwallet.seed == seed #now we have a functional wallet + file, update the cache; first try #with failed paths oldpath = newwallet.path newwallet.path = None newwallet.update_cache_index() newwallet.path = "fake-path-definitely-doesnt-exist" newwallet.update_cache_index() #with real path newwallet.path = oldpath newwallet.index = [[1, 1]] * 5 newwallet.update_cache_index() #ensure we cannot find a mainnet wallet from seed seed = "goodbye" jm_single().config.set("BLOCKCHAIN", "network", "mainnet") with pytest.raises(IOError) as e_info: Wallet(seed, 5, 6, False, False) load_program_config()
def main(): parser = get_sendpayment_parser() (options, args) = parser.parse_args() load_program_config() if options.schedule == '' and len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) #without schedule file option, use the arguments to create a schedule #of a single transaction sweeping = False if options.schedule == '': #note that sendpayment doesn't support fractional amounts, fractions throw #here. amount = int(args[1]) if amount == 0: sweeping = True destaddr = args[2] mixdepth = options.mixdepth addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print('ERROR: Address invalid. ' + errormsg) return schedule = [[ options.mixdepth, amount, options.makercount, destaddr, 0.0, 0 ]] else: result, schedule = get_schedule(options.schedule) if not result: log.info( "Failed to load schedule file, quitting. Check the syntax.") log.info("Error was: " + str(schedule)) sys.exit(0) mixdepth = 0 for s in schedule: if s[1] == 0: sweeping = True #only used for checking the maximum mixdepth required mixdepth = max([mixdepth, s[0]]) wallet_name = args[0] #to allow testing of confirm/unconfirm callback for multiple txs if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface): jm_single().bc_interface.tick_forward_chain_interval = 10 jm_single().maker_timeout_sec = 5 chooseOrdersFunc = None if options.pickorders: chooseOrdersFunc = pick_order if sweeping: print('WARNING: You may have to pick offers multiple times') print('WARNING: due to manual offer picking while sweeping') elif options.choosecheapest: chooseOrdersFunc = cheapest_order_choose else: # choose randomly (weighted) chooseOrdersFunc = weighted_order_choose # Dynamically estimate a realistic fee if it currently is the default value. # At this point we do not know even the number of our own inputs, so # we guess conservatively with 2 inputs and 2 outputs each if options.txfee == -1: options.txfee = max(options.txfee, estimate_tx_fee(2, 2)) log.debug("Estimated miner/tx fee for each cj participant: " + str(options.txfee)) assert (options.txfee >= 0) log.debug('starting sendpayment') if not options.userpcwallet: max_mix_depth = max([mixdepth, options.amtmixdepths]) if not os.path.exists(os.path.join('wallets', wallet_name)): wallet = Wallet(wallet_name, None, max_mix_depth, options.gaplimit) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") wallet = Wallet(wallet_name, pwd, max_mix_depth, options.gaplimit) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e)) sys.exit(0) break else: wallet = BitcoinCoreWallet(fromaccount=wallet_name) sync_wallet(wallet, fast=options.fastsync) if options.makercount == 0: if isinstance(wallet, BitcoinCoreWallet): raise NotImplementedError( "Direct send only supported for JM wallets") direct_send(wallet, amount, mixdepth, destaddr, options.answeryes) return def filter_orders_callback(orders_fees, cjamount): orders, total_cj_fee = orders_fees log.info("Chose these orders: " + pprint.pformat(orders)) log.info('total cj fee = ' + str(total_cj_fee)) total_fee_pc = 1.0 * total_cj_fee / cjamount log.info('total coinjoin fee = ' + str(float('%.3g' % (100.0 * total_fee_pc))) + '%') WARNING_THRESHOLD = 0.02 # 2% if total_fee_pc > WARNING_THRESHOLD: log.info('\n'.join(['=' * 60] * 3)) log.info('WARNING ' * 6) log.info('\n'.join(['=' * 60] * 1)) log.info( 'OFFERED COINJOIN FEE IS UNUSUALLY HIGH. DOUBLE/TRIPLE CHECK.') log.info('\n'.join(['=' * 60] * 1)) log.info('WARNING ' * 6) log.info('\n'.join(['=' * 60] * 3)) if not options.answeryes: if raw_input('send with these orders? (y/n):')[0] != 'y': return False return True def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None): if fromtx == "unconfirmed": #If final entry, stop *here*, don't wait for confirmation if taker.schedule_index + 1 == len(taker.schedule): reactor.stop() return if fromtx: if res: txd, txid = txdetails taker.wallet.remove_old_utxos(txd) taker.wallet.add_new_utxos(txd, txid) reactor.callLater(waittime * 60, clientfactory.getClient().clientStart) else: #a transaction failed; just stop reactor.stop() else: if not res: log.info("Did not complete successfully, shutting down") #Should usually be unreachable, unless conf received out of order; #because we should stop on 'unconfirmed' for last (see above) else: log.info("All transactions completed correctly") reactor.stop() taker = Taker(wallet, schedule, order_chooser=chooseOrdersFunc, callbacks=(filter_orders_callback, None, taker_finished)) clientfactory = JMTakerClientProtocolFactory(taker) nodaemon = jm_single().config.getint("DAEMON", "no_daemon") daemon = True if nodaemon == 1 else False start_reactor(jm_single().config.get("DAEMON", "daemon_host"), jm_single().config.getint("DAEMON", "daemon_port"), clientfactory, daemon=daemon)
def test_wallet_create(setup_wallets, includecache, wrongnet, storepwd, extendmd, pwdnumtries): walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet( ) assert wallet.get_key( 4, 1, 17) == "96095d7542e4e832c476b9df7e49ca9e5be61ad3bb8c8a3bdd8e141e2f4caf9101" assert wallet.get_addr(2, 0, 5) == "2NBUxbEQrGPKrYCV6d4o7Y4AtJ34Uy6gZZg" jm_single().bc_interface.wallet_synced = True assert wallet.get_new_addr(1, 0) == "2Mz817RE6zqywgkG2h9cATUoiXwnFSxufk2" assert wallet.get_external_addr(3) == "2N3gn65WXEzbLnjk5FLDZPc1pL6ebvZAmoA" addr3internal = wallet.get_internal_addr(3) assert addr3internal == "2N5NMTYogAyrGhDtWBnVQUp1kgwwFzcf7UM" assert wallet.get_key_from_addr( addr3internal) == "089a7173314d29f99e02a37e36da517ce41537a317c83284db1f33dda0af0cc201" dummyaddr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU" assert not wallet.get_key_from_addr(dummyaddr) #Make a new Wallet(), and prepare a testnet wallet file for this wallet password = "******" password_key = bitcoin.bin_dbl_sha256(password) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") net = get_network() if not wrongnet else 'mainnnet' walletfilejson = {'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': net} if includecache: mmd = wallet.max_mix_depth if not extendmd else wallet.max_mix_depth + 5 print("using mmd: " + str(mmd)) walletfilejson.update({'index_cache': [[0, 0]] * mmd}) walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if wrongnet: with pytest.raises(ValueError) as e_info: SegwitWallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) return from string import ascii_letters for i in range( pwdnumtries): #multiple tries to ensure pkcs7 error is triggered with pytest.raises(WalletError) as e_info: wrongpwd = "".join([random.choice(ascii_letters) for _ in range(20) ]) SegwitWallet(testwalletname, wrongpwd, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) with pytest.raises(WalletError) as e_info: SegwitWallet(testwalletname, None, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) newwallet = SegwitWallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) assert newwallet.seed == wallet.wallet_data_to_seed(seed) #now we have a functional wallet + file, update the cache; first try #with failed paths oldpath = newwallet.path newwallet.path = None newwallet.update_cache_index() newwallet.path = "fake-path-definitely-doesnt-exist" newwallet.update_cache_index() #with real path newwallet.path = oldpath newwallet.index = [[1, 1]] * 5 newwallet.update_cache_index() #ensure we cannot find a mainnet wallet from seed seed = "goodbye" jm_single().config.set("BLOCKCHAIN", "network", "mainnet") with pytest.raises(IOError) as e_info: Wallet(seed, 5, 6, False, False) load_program_config()
def main(): tumble_log = get_tumble_log(logsdir) (options, args) = get_tumbler_parser().parse_args() options = vars(options) if len(args) < 1: parser.error('Needs a wallet file') sys.exit(0) load_program_config() #Load the wallet wallet_name = args[0] max_mix_depth = options['mixdepthsrc'] + options['mixdepthcount'] if not os.path.exists(os.path.join('wallets', wallet_name)): wallet = Wallet(wallet_name, None, max_mix_depth) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") wallet = Wallet(wallet_name, pwd, max_mix_depth) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e)) sys.exit(0) break sync_wallet(wallet, fast=options['fastsync']) #Parse options and generate schedule #Output information to log files jm_single().mincjamount = options['mincjamount'] destaddrs = args[1:] print(destaddrs) #If the --restart flag is set we read the schedule #from the file, and filter out entries that are #already complete if options['restart']: res, schedule = get_schedule(os.path.join(logsdir, options['schedulefile'])) if not res: print("Failed to load schedule, name: " + str( options['schedulefile'])) print("Error was: " + str(schedule)) sys.exit(0) #This removes all entries that are marked as done schedule = [s for s in schedule if s[5] != 1] if isinstance(schedule[0][5], str) and len(schedule[0][5]) == 64: #ensure last transaction is confirmed before restart tumble_log.info("WAITING TO RESTART...") txid = schedule[0][5] restart_waiter(txid + ":0") #add 0 index because all have it #remove the already-done entry (this connects to the other TODO, #probably better *not* to truncate the done-already txs from file, #but simplest for now. schedule = schedule[1:] elif schedule[0][5] != 0: print("Error: first schedule entry is invalid.") sys.exit(0) with open(os.path.join(logsdir, options['schedulefile']), "wb") as f: f.write(schedule_to_text(schedule)) tumble_log.info("TUMBLE RESTARTING") else: #Create a new schedule from scratch schedule = get_tumble_schedule(options, destaddrs) tumble_log.info("TUMBLE STARTING") with open(os.path.join(logsdir, options['schedulefile']), "wb") as f: f.write(schedule_to_text(schedule)) print("Schedule written to logs/" + options['schedulefile']) tumble_log.info("With this schedule: ") tumble_log.info(pprint.pformat(schedule)) print("Progress logging to logs/TUMBLE.log") def filter_orders_callback(orders_fees, cjamount): """Decide whether to accept fees """ return tumbler_filter_orders_callback(orders_fees, cjamount, taker, options) def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None): """on_finished_callback for tumbler; processing is almost entirely deferred to generic taker_finished in tumbler_support module, except here reactor signalling. """ sfile = os.path.join(logsdir, options['schedulefile']) tumbler_taker_finished_update(taker, sfile, tumble_log, options, res, fromtx, waittime, txdetails) if not fromtx: reactor.stop() elif fromtx != "unconfirmed": reactor.callLater(waittime*60, clientfactory.getClient().clientStart) #to allow testing of confirm/unconfirm callback for multiple txs if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface): jm_single().bc_interface.tick_forward_chain_interval = 10 jm_single().maker_timeout_sec = 5 #instantiate Taker with given schedule and run taker = Taker(wallet, schedule, order_chooser=weighted_order_choose, callbacks=(filter_orders_callback, None, taker_finished), tdestaddrs=destaddrs) clientfactory = JMTakerClientProtocolFactory(taker) nodaemon = jm_single().config.getint("DAEMON", "no_daemon") daemon = True if nodaemon == 1 else False start_reactor(jm_single().config.get("DAEMON", "daemon_host"), jm_single().config.getint("DAEMON", "daemon_port"), clientfactory, daemon=daemon)
def main_cs(test_data=None): #twisted logging (TODO disable for non-debug runs) if test_data: wallet_name, args, options, use_ssl, alt_class, alt_c_class, fail_alice_state, fail_carol_state = test_data server, port, usessl = parse_server_string(options.serverport) else: parser = get_coinswap_parser() (options, args) = parser.parse_args() #Will only be used by client server, port, usessl = parse_server_string(options.serverport) if options.checkonly: #no need for any more data; just query alice_client = CoinSwapJSONRPCClient(server[2:], port, usessl=usessl) reactor.callWhenRunning(alice_client.send_poll_unsigned, "status", print_status) reactor.run() return log.startLogging(sys.stdout) load_coinswap_config() wallet_name = args[0] #depth 0: spend in, depth 1: receive out, depth 2: for backout transactions. max_mix_depth = 3 wallet_dir = os.path.join(cs_single().homedir, 'wallets') if not os.path.exists(os.path.join(wallet_dir, wallet_name)): wallet = Wallet(wallet_name, None, max_mix_depth, 6, wallet_dir=wallet_dir) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") wallet = Wallet(wallet_name, pwd, max_mix_depth, 6, wallet_dir=wallet_dir) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e)) sys.exit(0) break #for testing main script (not test framework), need funds. if not test_data and isinstance( cs_single().bc_interface, RegtestBitcoinCoreInterface): cs_single().bc_interface.grab_coins(wallet.get_new_addr(0, 0, True), 2.0) wallet.index[0][0] -= 1 time.sleep(3) sync_wallet(wallet, fast=options.fastsync) if test_data: cs_single().bc_interface.wallet_synced = True wallet.used_coins = None if options.serve: #sanity check that client params were not provided: if len(args) > 1: print("Extra parameters provided for running as server. " "Are you sure you didn't want to run as client?") sys.exit(0) if not test_data: main_server(options, wallet) else: main_server(options, wallet, {'use_ssl': use_ssl, 'alt_c_class': alt_c_class, 'fail_carol_state': fail_carol_state}) return wallet.get_balance_by_mixdepth() return if not options.recover: target_amount = int(args[1]) #Reset the targetting for backout transactions #TODO must be removed/changed for updated fees handling oldtarget = cs_single().config.get("POLICY", "tx_fees") newtarget = cs_single().config.getint("POLICY", "backout_fee_target") multiplier = float(cs_single().config.get("POLICY", "backout_fee_multiplier")) cs_single().config.set("POLICY", "tx_fees", str(newtarget)) tx23fee = estimate_tx_fee((1, 2, 2), 1, txtype='p2shMofN') tx23fee = int(multiplier * tx23fee) tx24_recipient_amount = target_amount - tx23fee tx35_recipient_amount = target_amount - tx23fee cs_single().config.set("POLICY", "tx_fees", oldtarget) #to allow testing of confirm/unconfirm callback for multiple txs if isinstance(cs_single().bc_interface, RegtestBitcoinCoreInterface): cs_single().bc_interface.tick_forward_chain_interval = 2 cs_single().bc_interface.simulating = True cs_single().config.set("BLOCKCHAIN", "notify_port", "62652") cs_single().config.set("BLOCKCHAIN", "rpc_host", "127.0.0.2") #if restart option selected, read state and backout if options.recover: session_id = options.recover alice = CoinSwapAlice(wallet, 'alicestate') alice.bbmb = wallet.get_balance_by_mixdepth(verbose=False) alice.load(sessionid=session_id) alice.backout("Recovering from shutdown") reactor.run() return if len(args) > 2: tx5address = args[2] if not validate_address(tx5address): print("Invalid address: ", tx5address) sys.exit(0) else: #Our destination address should be in a separate mixdepth tx5address = wallet.get_new_addr(1, 1, True) #instantiate the parameters, but don't yet have the ephemeral pubkeys #or destination addresses. #TODO figure out best estimate incl. priority btcfee_est = estimate_tx_fee((1, 2, 2), 1, txtype='p2shMofN') cpp = CoinSwapPublicParameters(base_amount=target_amount, bitcoin_fee=btcfee_est) cpp.set_addr_data(addr5=tx5address) testing_mode = True if test_data else False aliceclass = alt_class if test_data and alt_class else CoinSwapAlice if test_data and fail_alice_state: alice = aliceclass(wallet, 'alicestate', cpp, testing_mode=testing_mode, fail_state=fail_alice_state) else: alice = aliceclass(wallet, 'alicestate', cpp, testing_mode=testing_mode) alice_client = CoinSwapJSONRPCClient(server[2:], port, alice.sm.tick, alice.backout, usessl) alice.set_jsonrpc_client(alice_client) reactor.callWhenRunning(alice_client.send_poll_unsigned, "status", alice.check_server_status) if not test_data: reactor.run() if test_data: return alice
noscan_methods = ['showseed', 'importprivkey', 'dumpprivkey'] if len(args) < 1: parser.error('Needs a wallet file or method') sys.exit(0) load_coinswap_config() if args[0] in noseed_methods: method = args[0] else: seed = args[0] method = ('display' if len(args) == 1 else args[1].lower()) if not os.path.exists(os.path.join('wallets', seed)): wallet = Wallet(seed, None, options.maxmixdepth, options.gaplimit, extend_mixdepth= not maxmixdepth_configured, storepassword=(method == 'importprivkey')) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") wallet = Wallet(seed, pwd, options.maxmixdepth, options.gaplimit, extend_mixdepth=not maxmixdepth_configured, storepassword=(method == 'importprivkey')) except WalletError: print("Wrong password, try again.") continue except Exception as e: print("Failed to load wallet, error message: " + repr(e))