def main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a payment from your wallet to an given address' + ' using coinjoin. First acts as a maker, announcing an order and ' + 'waiting for someone to fill it. After a set period of time, gives' + ' up waiting and acts as a taker and coinjoins any remaining coins.' + ' NOTE: In the current state of JoinMarket software, this script' + ' only works if your JoinMarket wallet contains the private key of your' + ' destination address. So you can only send to yourself and you need' + ' to import the privkey') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='miner fee contribution, in satoshis, default=10000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help= 'how many makers to coinjoin with when taking liquidity, default=2', default=2) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, default=8', default=8) parser.add_option( '-c', '--cjfee', action='store', type='int', dest='cjfee', help= 'coinjoin fee asked for when being a maker, in satoshis per order filled, default=50000', default=50000) parser.add_option('-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the internal joinmarket ' + 'wallet. Requires blockchain_source=json-rpc') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print 'ERROR: Address invalid. ' + errormsg return waittime = timedelta(hours=options.waittime).total_seconds() print 'txfee=%d cjfee=%d waittime=%s makercount=%d' % ( options.txfee, options.cjfee, str( timedelta(hours=options.waittime)), options.makercount) # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) jm_single().bc_interface.sync_wallet(wallet) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] if available_balance < amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return jm_single().nickname = random_nick() log.debug('Running patient sender of a payment') irc = IRCMessageChannel(jm_single().nickname) PatientSendPayment(irc, wallet, destaddr, amount, options.makercount, options.txfee, options.cjfee, waittime, options.mixdepth) try: irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()
#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 main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file / fromaccount] [amount] [destaddr]', description='Sends a payment from your wallet to an given address' + ' using coinjoin. First acts as a maker, announcing an order and ' + 'waiting for someone to fill it. After a set period of time, gives' + ' up waiting and acts as a taker and coinjoins any remaining coins') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=10000, help='miner fee contribution, in satoshis, default=10000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help= 'how many makers to coinjoin with when taking liquidity, default=2', default=2) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, default=8', default=8) parser.add_option( '-c', '--cjfee', action='store', type='int', dest='cjfee', help= 'coinjoin fee asked for when being a maker, in satoshis per order filled, default=50000', default=50000) parser.add_option( '-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the internal joinmarket ' + 'wallet. Requires blockchain_source=json-rpc') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] amount = int(args[1]) destaddr = args[2] load_program_config() addr_valid, errormsg = validate_address(destaddr) if not addr_valid: print 'ERROR: Address invalid. ' + errormsg return waittime = timedelta(hours=options.waittime).total_seconds() print 'txfee=%d cjfee=%d waittime=%s makercount=%d' % ( options.txfee, options.cjfee, str(timedelta(hours=options.waittime)), options.makercount) # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) jm_single().bc_interface.sync_wallet(wallet) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] if available_balance < amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return jm_single().nickname = random_nick() log.debug('Running patient sender of a payment') irc = IRCMessageChannel(jm_single().nickname) PatientSendPayment(irc, wallet, destaddr, amount, options.makercount, options.txfee, options.cjfee, waittime, options.mixdepth) try: irc.run() except: log.debug('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()
print(' %s best block is %s' % (datetime.datetime.fromtimestamp(now) .strftime("%Y-%m-%d %H:%M"), bestblockhash)) 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=0.1, b=-0.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 main(): parser = OptionParser( usage= 'usage: %prog [options] [wallet file] [[dest..] [amount]..]', description='Sends a payment from your wallet to an given address' + ' using coinjoin but for users who dont mind ' + 'waiting. First acts as a maker, announcing an order' + ' and waiting for someone to fill it. After a set ' + 'period of time, gives up waiting and acts as a taker' + ' and coinjoins any remaining coins. Is able to send' + ' to multiple locations one after another. [dest] ' + 'can be multiple addresses or a xpub BIP32 key. xpub' + ' keys can be optionally followed with :index to ' + 'start from another address than zero') parser.add_option( '-f', '--txfee', action='store', type='int', dest='txfee', default=1000, help='miner fee contribution, in satoshis, default=1000') parser.add_option( '-N', '--makercount', action='store', type='int', dest='makercount', help='how many makers to coinjoin with, default random ' 'from 5 to 7', default=random.randint(5, 7)) parser.add_option( '-w', '--wait-time', action='store', type='float', dest='waittime', help='wait time in hours as a maker before becoming a taker, ' + 'or zero to wait forever, default=8', default=8) parser.add_option( '-c', '--base-cjfee', action='store', type='int', dest='cjfee_base', help= 'base coinjoin fee asked for when being a maker, in satoshis per' + ' order filled, default=500', default=500) parser.add_option( '-a', '--add-cjfee', action='store', type='int', dest='cjfee_add', help= 'additional coinjoin fee asked for when being a maker when ' + 'coinjoin amount not exact, in satoshis per order filled' + ', default=1000', default=1000) parser.add_option( '-m', '--mixdepth', action='store', type='int', dest='mixdepth', help='mixing depth to spend from, default=0', default=0) parser.add_option( '--rpcwallet', action='store_true', dest='userpcwallet', default=False, help= 'Use the Bitcoin Core wallet through json rpc, instead of the ' + 'internal joinmarket wallet. Requires blockchain_source=json-rpc.' + ' NOT IMPLEMENTED YET') 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')) parser.add_option( '-x', '--maxcjfee', type='float', dest='maxcjfee', nargs=2, default=(0.01, 10000), help='maximum coinjoin fee and bitcoin value the taker is ' + 'willing to pay to a single market maker. Both values need' + ' to be exceeded, so if the fee is 30% but only 500satoshi ' + 'is paid the tx will go ahead. default=0.01, 10000 ' + '(1%, 10000satoshi)') parser.add_option( '-q', '--liquiditywait', type='int', dest='liquiditywait', default=20, help= 'amount of seconds to wait after failing to choose suitable orders' ' before trying again, default 20') parser.add_option( '-u', '--minoutputsize', type='int', dest='minoutputsize', nargs=1, default=30000, help='minimum size of output in satoshis produced by ' 'patientsendpayment. default=30000 satoshi') (options, args) = parser.parse_args() if len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) wallet_name = args[0] load_program_config() send_jobs = [] destination = None for ar in args[1:]: if ar.isdigit(): if destination == None: log.error('found amount without destination') return elif isinstance(destination, list): send_jobs.append( {'amount': int(ar), 'addresses': destination, 'index': 0} ) elif isinstance(destination, tuple): send_jobs.append( {'amount': int(ar), 'xpub': destination[0] , 'index': destination[1]} ) else: assert False destination = None else: if validate_address(ar)[0]: if destination == None: destination = [] destination.append(ar) else: index = 0 colon = ar.find(':') if colon > -1: index = int(ar[colon+1:]) ar = ar[:colon] if is_bip32_pubkey(ar): destination = (ar, index) else: log.error('unable to parse destination: ' + ar) return if destination != None: log.error('missing amount') return for j in send_jobs: print('sending ' + str(j['amount']) + ' satoshi to: ') if 'addresses' in j: for a in j['addresses']: print(' ' + get_next_address(j)) else: print(' ' + j['xpub'] + '\n starting from index: ' + str(j['index']) + '. first 5 addresses:') index_cache = j['index'] for i in range(5): print(' ' + get_next_address(j)) j['index'] = index_cache waittime = timedelta(hours=options.waittime).total_seconds() # todo: this section doesn't make a lot of sense if not options.userpcwallet: wallet = Wallet(wallet_name, options.mixdepth + 1) else: print 'not implemented yet' sys.exit(0) # wallet = BitcoinCoreWallet(fromaccount=wallet_name) sync_wallet(wallet, fast=options.fastsync) available_balance = wallet.get_balance_by_mixdepth()[options.mixdepth] total_amount = sum((j['amount'] for j in send_jobs)) if available_balance < total_amount: print 'not enough money at mixdepth=%d, exiting' % options.mixdepth return log.info('Running patient sender of a payment') mcs = [IRCMessageChannel(c) for c in get_irc_mchannels()] mcc = MessageChannelCollection(mcs) PatientSendPayment(mcc, wallet, send_jobs, options, waittime) try: mcc.run() except: log.warn('CRASHING, DUMPING EVERYTHING') debug_dump_object(wallet, ['addr_cache', 'keys', 'seed']) # todo: looks wrong. dump on the class object? # debug_dump_object(taker) import traceback traceback.print_exc()