def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """ Start a bitcoind and return RPC connection to it """ datadir = os.path.join(dirname, "node"+str(i)) if binary is None: binary = os.getenv("BITCOIND", "bitcoind") args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ] if extra_args is not None: args.extend(extra_args) bitcoind_processes[i] = subprocess.Popen(args) devnull = open(os.devnull, "w") if os.getenv("PYTHON_DEBUG", ""): print "start_node: bitcoind started, calling bitcoin-cli -rpcwait getblockcount" subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir] + _rpchost_to_args(rpchost) + ["-rpcwait", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "start_node: calling bitcoin-cli -rpcwait getblockcount returned" devnull.close() url = "http://*****:*****@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i)) if timewait is not None: proxy = AuthServiceProxy(url, timeout=timewait) else: proxy = AuthServiceProxy(url) proxy.url = url # store URL on proxy for info return proxy
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """ Start a komodod and return RPC connection to it """ datadir = os.path.join(dirname, "node"+str(i)) # creating special config in case of cryptocondition asset chain test if extra_args[0] == '-ac_name=REGTEST': configpath = datadir + "/REGTEST.conf" with open(configpath, "w+") as config: config.write("regtest=1\n") config.write("rpcuser=rt\n") config.write("rpcpassword=rt\n") port = extra_args[3] config.write("rpcport=" + (port[9:]) + "\n") config.write("server=1\n") config.write("txindex=1\n") config.write("rpcworkqueue=256\n") config.write("rpcallowip=127.0.0.1\n") config.write("bind=127.0.0.1\n") config.write("rpcbind=127.0.0.1") if binary is None: binary = os.getenv("BITCOIND", "komodod") args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ] if extra_args is not None: args.extend(extra_args) #print("args=" + ' '.join(args)) bitcoind_processes[i] = subprocess.Popen(args) devnull = open("/dev/null", "w+") cmd = os.getenv("BITCOINCLI", "komodo-cli") print("cmd=" + cmd) cmd_args = ' '.join(extra_args) + " -rpcwait getblockcount " if os.getenv("PYTHON_DEBUG", ""): print "start_node: komodod started, calling : " + cmd + " " + cmd_args strcmd = cmd + " " + cmd_args print("Running " + strcmd) import time time.sleep(2) subprocess.check_call(strcmd, shell=True); #subprocess.check_call([ os.getenv("BITCOINCLI", "komodo-cli"), "-datadir="+datadir] + # _rpchost_to_args(rpchost) + # ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "start_node: calling komodo-cli -rpcwait getblockcount returned" devnull.close() port = extra_args[3] url = "http://*****:*****@%s:%d" % (rpchost or '127.0.0.1', int(port[9:])) print("connecting to " + url) if timewait is not None: proxy = AuthServiceProxy(url, timeout=timewait) else: proxy = AuthServiceProxy(url) print("created proxy") proxy.url = url # store URL on proxy for info return proxy
def initialize_chain(test_dir): """ Create (or copy from cache) a 200-block-long chain and 4 wallets. bitcoind and bitcoin-cli must be in search path. """ if not os.path.isdir(os.path.join("cache", "node0")): devnull = open("/dev/null", "w+") # Create cache directories, run bitcoinds: for i in range(4): datadir=initialize_datadir("cache", i) args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir="+datadir, "-discover=0" ] if i > 0: args.append("-connect=127.0.0.1:"+str(p2p_port(0))) bitcoind_processes[i] = subprocess.Popen(args) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: bitcoind started, calling bitcoin-cli -rpcwait getblockcount" subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir, "-rpcwait", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: bitcoin-cli -rpcwait getblockcount completed" devnull.close() rpcs = [] for i in range(4): try: url = "http://*****:*****@127.0.0.1:%d"%(rpc_port(i),) rpcs.append(AuthServiceProxy(url)) except: sys.stderr.write("Error connecting to "+url+"\n") sys.exit(1) # Create a 200-block-long chain; each of the 4 nodes # gets 25 mature blocks and 25 immature. # blocks are created with timestamps 10 minutes apart, starting # at 1 Jan 2014 block_time = 1388534400 for i in range(2): for peer in range(4): for j in range(25): set_node_times(rpcs, block_time) rpcs[peer].generate(1) block_time += 10*60 # Must sync before next peer starts generating blocks sync_blocks(rpcs) # Shut them down, and clean up cache directories: stop_nodes(rpcs) wait_bitcoinds() for i in range(4): os.remove(log_filename("cache", i, "debug.log")) os.remove(log_filename("cache", i, "db.log")) os.remove(log_filename("cache", i, "peers.dat")) os.remove(log_filename("cache", i, "fee_estimates.dat")) for i in range(4): from_dir = os.path.join("cache", "node"+str(i)) to_dir = os.path.join(test_dir, "node"+str(i)) shutil.copytree(from_dir, to_dir) initialize_datadir(test_dir, i) # Overwrite port/rpcport in asofe.conf
def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.singleScreenMode = settings['single-screen-mode']
def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.candy_price = settings['candy_price'] self.candy_currency = settings['candy_currency'] self.arduino_port = settings['arduino_port'] self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = ""
def start_bitcoind(bitcoind_path): datadir = tempfile.mkdtemp() bitcoind_proc = subprocess.Popen([bitcoind_path, '-regtest', '-datadir=' + datadir, '-noprinttoconsole', '-fallbackfee=0.0002']) def cleanup_bitcoind(): bitcoind_proc.kill() shutil.rmtree(datadir) atexit.register(cleanup_bitcoind) # Wait for cookie file to be created while not os.path.exists(datadir + '/regtest/.cookie'): time.sleep(0.5) # Read .cookie file to get user and pass with open(datadir + '/regtest/.cookie') as f: userpass = f.readline().lstrip().rstrip() rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(userpass)) # Wait for bitcoind to be ready ready = False while not ready: try: rpc.getblockchaininfo() ready = True except JSONRPCException: time.sleep(0.5) pass # Make sure there are blocks and coins available rpc.generatetoaddress(101, rpc.getnewaddress()) return (rpc, userpass)
def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.expected_amount = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses']
def start(self): def get_free_port(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 0)) s.listen(1) port = s.getsockname()[1] s.close() return port self.p2p_port = get_free_port() self.rpc_port = get_free_port() self.bitcoind_proc = subprocess.Popen([ self.bitcoind_path, "-regtest", f"-datadir={self.datadir}", "-noprinttoconsole", "-fallbackfee=0.0002", "-keypool=1", f"-port={self.p2p_port}", f"-rpcport={self.rpc_port}" ]) atexit.register(self.cleanup) # Wait for cookie file to be created cookie_path = os.path.join(self.datadir, "regtest", ".cookie") while not os.path.exists(cookie_path): time.sleep(0.5) # Read .cookie file to get user and pass with open(cookie_path) as f: self.userpass = f.readline().lstrip().rstrip() self.rpc_url = f"http://{self.userpass}@127.0.0.1:{self.rpc_port}" self.rpc = AuthServiceProxy(self.rpc_url) # Wait for bitcoind to be ready ready = False while not ready: try: self.rpc.getblockchaininfo() ready = True except JSONRPCException: time.sleep(0.5) pass # Make sure there are blocks and coins available self.rpc.createwallet(wallet_name="supply") self.wrpc = self.get_wallet_rpc("supply") self.wrpc.generatetoaddress(101, self.wrpc.getnewaddress())
def __init__(self, settings, nfc_broadcast): self.nfc_broadcast = nfc_broadcast self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses'] self.bt_addr = None
def run_allowip_test(self, allow_ips, rpchost, rpcport): ''' Start a node with rpcwallow IP, and request getinfo at a non-localhost IP. ''' base_args = ['-disablewallet', '-nolisten' ] + ['-rpcallowip=' + x for x in allow_ips] nodes = start_nodes(1, self.options.tmpdir, [base_args]) try: # connect to node through non-loopback interface url = "http://*****:*****@%s:%d" % ( rpchost, rpcport, ) node = AuthServiceProxy(url) node.getinfo() finally: node = None # make sure connection will be garbage collected and closed stop_nodes(nodes) wait_bitcoinds()
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """ Start a zend and return RPC connection to it """ datadir = os.path.join(dirname, "node" + str(i)) if binary is None: # ZEN_MOD_START binary = os.getenv("BITCOIND", "zend") # ZEN_MOD_END args = [ binary, "-datadir=" + datadir, "-keypool=1", "-discover=0", "-rest" ] if extra_args is not None: args.extend(extra_args) bitcoind_processes[i] = subprocess.Popen(args) devnull = open("/dev/null", "w+") # ZEN_MOD_START if os.getenv("PYTHON_DEBUG", ""): print "start_node: zend started, calling zen-cli -rpcwait getblockcount" subprocess.check_call( [os.getenv("BITCOINCLI", "zen-cli"), "-datadir=" + datadir] + _rpchost_to_args(rpchost) + ["-rpcwait", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "start_node: calling zen-cli -rpcwait getblockcount returned" # ZEN_MOD_END devnull.close() url = "http://*****:*****@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i)) if timewait is not None: proxy = AuthServiceProxy(url, timeout=timewait) else: proxy = AuthServiceProxy(url) proxy.url = url # store URL on proxy for info return proxy
def __init__(self, options, instance_name): self.options = options self.instance_name = instance_name handlers = [ (r"/api/receive", ReceiveHandler), (r"/api/walletnotify/(?P<txid>[^\/]+)", WalletNotifyHandler), (r"/api/blocknotify/(?P<hash>[^\/]+)", BlockNotifyHandler), ] settings = dict(cookie_secret='cookie_secret') tornado.web.Application.__init__(self, handlers, **settings) input_log_file_handler = logging.handlers.TimedRotatingFileHandler( self.options.log, when='MIDNIGHT') formatter = logging.Formatter('%(asctime)s - %(message)s') input_log_file_handler.setFormatter(formatter) self.bitcoind = AuthServiceProxy(self.options.rpc_url) self.paytxfee = self.bitcoind.getinfo()['paytxfee'] self.replay_logger = logging.getLogger(self.instance_name) self.replay_logger.setLevel(logging.DEBUG) self.replay_logger.addHandler(input_log_file_handler) self.replay_logger.info('START') ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.DEBUG) ch.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) self.replay_logger.addHandler(ch) from models import Base, db_bootstrap engine = create_engine(self.options.db_engine, echo=self.options.db_echo) Base.metadata.create_all(engine) self.db_session = scoped_session(sessionmaker(bind=engine)) db_bootstrap(self.db_session) self.log_start_data()
def setUp(self): self.rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(self.rpc_userpass)) if '{}_test'.format(self.full_type) not in self.rpc.listwallets(): self.rpc.createwallet('{}_test'.format(self.full_type), True) self.wrpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/{}_test'.format(self.rpc_userpass, self.full_type)) self.wpk_rpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/'.format(self.rpc_userpass)) if '--testnet' not in self.dev_args: self.dev_args.append('--testnet') self.emulator.start()
def __init__(self): # load config: Config = ConfigParser.ConfigParser(allow_no_value = True) Config.read('config.cfg') network = Config.get('Global','network') rpcusername = Config.get(network,'rpcusername') rpcpassword = Config.get(network,'rpcpassword') rpcport = Config.getint(network,'rpcport') rpchostip = Config.get(network,'rpchostip') self.plotpath = str(Config.get(network,'htmlpath')) self.block_window = Config.getint(network,'block_window') self.block_domain = Config.getint(network,'block_domain') # set rpc: self.rpc = AuthServiceProxy('http://'+rpcusername+':'+rpcpassword+'@'+ rpchostip+':'+str(rpcport))
def get_rpc_proxy(url, node_number, timeout=None): """ Args: url (str): URL of the RPC server to call node_number (int): the node number (or id) that this calls to Kwargs: timeout (int): HTTP timeout in seconds Returns: AuthServiceProxy. convenience object for making RPC calls. """ proxy_kwargs = {} if timeout is not None: proxy_kwargs['timeout'] = timeout proxy = AuthServiceProxy(url, **proxy_kwargs) proxy.url = url # store URL on proxy for info coverage_logfile = coverage.get_filename( COVERAGE_DIR, node_number) if COVERAGE_DIR else None return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
def initialize_chain(test_dir): """ Create (or copy from cache) a 200-block-long chain and 4 wallets. zend and zen-cli must be in search path. """ if os.path.isdir(os.path.join("cache", "node0")): if os.stat("cache").st_mtime + (60 * 60) < time.time(): print("initialize_chain(): Removing stale cache") shutil.rmtree("cache") if not os.path.isdir(os.path.join("cache", "node0")): devnull = open("/dev/null", "w+") # Create cache directories, run bitcoinds: for i in range(4): datadir=initialize_datadir("cache", i) args = [ os.getenv("BITCOIND", "zend"), "-keypool=1", "-datadir="+datadir, "-discover=0", "-rpcservertimeout=600" ] if i > 0: args.append("-connect=127.0.0.1:"+str(p2p_port(0))) bitcoind_processes[i] = subprocess.Popen(args) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: zend started, calling zen-cli -rpcwait getblockcount" subprocess.check_call([ os.getenv("BITCOINCLI", "zen-cli"), "-datadir="+datadir, "-rpcwait", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: zen-cli -rpcwait getblockcount completed" devnull.close() rpcs = [] for i in range(4): try: url = "http://*****:*****@127.0.0.1:%d"%(rpc_port(i),) rpcs.append(AuthServiceProxy(url)) except: sys.stderr.write("Error connecting to "+url+"\n") sys.exit(1) # Create a 200-block-long chain; each of the 4 nodes # gets 25 mature blocks and 25 immature. # blocks are created with timestamps 10 minutes apart, starting # at Fri, 12 May 2017 00:15:50 GMT (genesis block time) # block_time = 1494548150 block_time = int(time.time()) - (200 * 150) # 200 is number of blocks and 150 is blocktime target spacing 150 / 60 = 2.5 min for i in range(2): for peer in range(4): for j in range(25): set_node_times(rpcs, block_time) rpcs[peer].generate(1) # block_time += 10*60 # this was BTC target spacing ?? block_time += 150 # ZEN blocktime target spacing # Must sync before next peer starts generating blocks sync_blocks(rpcs) # Check that local time isn't going backwards assert_greater_than(time.time() + 1, block_time) # Shut them down, and clean up cache directories: stop_nodes(rpcs) wait_bitcoinds() for i in range(4): os.remove(log_filename("cache", i, "debug.log")) os.remove(log_filename("cache", i, "db.log")) os.remove(log_filename("cache", i, "peers.dat")) os.remove(log_filename("cache", i, "fee_estimates.dat")) for i in range(4): from_dir = os.path.join("cache", "node"+str(i)) to_dir = os.path.join(test_dir, "node"+str(i)) shutil.copytree(from_dir, to_dir) initialize_datadir(test_dir, i) # Overwrite port/rpcport in zen.conf
class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.expected_amount = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect(self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect(self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay(os.environ['POS'] + '/data/customer_display.html', self.single_screen_mode) if not self.single_screen_mode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.single_screen_mode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % (cur_amount, currency, self.exchange_rate, currency, self.exchange_rate_source) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) self.expected_amount = '%s BTC' % amount_str imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ (self.expected_amount, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid): if not hasattr(self, 'app'): return # not yet read # emit signal, so we can process this on the Qt GUI thread self.app.emit(QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), txid) def _new_transaction_received(self, txid): # check if we are waiting for a payment if self.current_address == "": return # check if the txid looks sane before passing it # to bitcoind (for security reasons; might be overly # paranoid, but can't hurt) if re.search("^[a-f0-9]*$", txid) == None: return tx_info = self.bitcoind.gettransaction(txid) address_found = False for detail in tx_info['details']: if self.current_address == detail['address']: amount_received = detail['amount'] address_found = True if not address_found: return msg = "Transaction to %s with amount %s (of %s expected) received." % (self.current_address, amount_received, self.expected_amount) (from_green_address, green_address_msg) = self.green_address_check(txid) if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def green_address_check(self, txid): found = False msg = "" origins = self.get_origins(txid) for origin in origins: if origin in self.green_addresses: found = True msg = self.green_addresses[origin] break return (found, msg) def get_origins(self, txid): try: origins = [] raw_tx = self.bitcoind.getrawtransaction(txid, 1) vins = raw_tx['vin'] for vin in vins: raw_tx = self.bitcoind.getrawtransaction(vin['txid'], 1) for vout in raw_tx['vout']: if vin['vout'] == vout['n']: origins.extend(vout['scriptPubKey']['addresses']) return origins except JSONRPCException: return [] def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): if not hasattr(self, 'app'): return # not yet read self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
class UpMyFee(): def __init__(self, service_url, wallet_unlock_timeout): self.wallet_unlock_timeout = wallet_unlock_timeout self.api = AuthServiceProxy(service_url=service_url, verify=False) def get_tx_amount(self, txid): return self.api.gettransaction(txid)['amount'] def get_new_tx(self, orig_tx, payer, to, fee): new_amount = 0 orig_amount = 0 if len(orig_tx['vout']) > 2: raise BaseException('Multiaddresses is NOT supported') orig_vins = [{ 'txid': t['txid'], 'vout': t['vout'] } for t in orig_tx['vin']] vins_sum_amounts = sum( [self.get_tx_amount(t['txid']) for t in orig_vins]) orig_vout = orig_tx['vout'] vouts_sum_amounts = sum(v['value'] for v in orig_vout) orig_fee = vins_sum_amounts - vouts_sum_amounts if orig_fee >= fee: raise BaseException('New tx fee should be more than the original!') for v in orig_vout: addresses = v['scriptPubKey']['addresses'] if len(addresses) > 1: raise BaseException('Multiaddresses is NOT supported') if payer not in [v['scriptPubKey']['addresses'][0] for v in orig_vout]: raise BaseException('Payer address not in transaction') fee_diff = fee - orig_fee new_vouts = {} for vout in orig_vout: addr = vout['scriptPubKey']['addresses'][0] amount = vout['value'] if addr == payer: orig_amount = amount amount -= fee_diff new_amount = amount new_vouts[to] = new_amount else: new_vouts[addr] = amount if new_amount <= Decimal(0): raise BaseException('New fee is very big!') return orig_vins, new_vouts, orig_fee, fee_diff, orig_amount, new_amount def get_user_confirm(self, payer, to, txid, fee, orig_fee, fee_diff, orig_amount, new_amount): print("Transaction ID:\t%s" % txid) print("Payer address:\t%s" % payer) print("New recipient:\t%s" % to) print("Orig fee:\t%s" % orig_fee) print("New fee:\t%s" % fee) print("Diff fee:\t%s" % fee_diff) print("Orig amount:\t%s" % orig_amount) print("New amount:\t%s" % new_amount) user_result = input("All is correct? (yes/no): ") if user_result != "yes": print('Exit.') return False return True def change_fee(self, payer, to, txid, fee, debug): orig_rawtx = self.api.getrawtransaction(txid) orig_tx = self.api.decoderawtransaction(orig_rawtx) vin, vout, orig_fee, fee_diff, orig_amount, new_amount = self.get_new_tx( orig_tx, payer, to, fee) new_rawtx = self.api.createrawtransaction(vin, vout) new_tx = self.api.decoderawtransaction(new_rawtx) if debug: pprint(new_tx) if not self.get_user_confirm(payer, to, txid, fee, orig_fee, fee_diff, orig_amount, new_amount): return False passphrase = input("Please enter the wallet passphrase: ") self.api.walletpassphrase(passphrase, self.wallet_unlock_timeout) tx_sign_result = self.api.signrawtransaction(new_rawtx) if not tx_sign_result['complete']: raise BaseException('Error sign transaction: %s' % str(tx_sign_result)) tx_signed_hex = tx_sign_result['hex'] tx_signed = self.api.decoderawtransaction(tx_signed_hex) if debug: pprint(tx_signed) print('HEX of signed transaction: ') print(print(tx_signed_hex)) print( 'You can decode and broadcast transaction use https://blockchain.info/pushtx' ) user_result = input( "Broadcast transaction using your Bitcoin node? (yes/no): ") if user_result != "yes": print('Exit.') return False sent_txid = self.api.sendrawtransaction(tx_signed_hex) print('New TxID: %s' % sent_txid)
class TestSignTx(DeviceTestCase): def setUp(self): self.rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(self.rpc_userpass)) if '{}_test'.format(self.full_type) not in self.rpc.listwallets(): self.rpc.createwallet('{}_test'.format(self.full_type), True) self.wrpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/{}_test'.format(self.rpc_userpass, self.full_type)) self.wpk_rpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/'.format(self.rpc_userpass)) if '--testnet' not in self.dev_args: self.dev_args.append('--testnet') self.emulator.start() def tearDown(self): self.emulator.stop() def _generate_and_finalize(self, unknown_inputs, psbt): if not unknown_inputs: # Just do the normal signing process to test "all inputs" case sign_res = self.do_command(self.dev_args + ['signtx', psbt['psbt']]) finalize_res = self.wrpc.finalizepsbt(sign_res['psbt']) else: # Sign only input one on first pass # then rest on second pass to test ability to successfully # ignore inputs that are not its own. Then combine both # signing passes to ensure they are actually properly being # partially signed at each step. first_psbt = PSBT() first_psbt.deserialize(psbt['psbt']) second_psbt = PSBT() second_psbt.deserialize(psbt['psbt']) # Blank master fingerprint to make hww fail to sign # Single input PSBTs will be fully signed by first signer for psbt_input in first_psbt.inputs[1:]: for pubkey, path in psbt_input.hd_keypaths.items(): psbt_input.hd_keypaths[pubkey] = (0,) + path[1:] for pubkey, path in second_psbt.inputs[0].hd_keypaths.items(): second_psbt.inputs[0].hd_keypaths[pubkey] = (0,) + path[1:] single_input = len(first_psbt.inputs) == 1 # Process the psbts first_psbt = first_psbt.serialize() second_psbt = second_psbt.serialize() # First will always have something to sign first_sign_res = self.do_command(self.dev_args + ['signtx', first_psbt]) self.assertTrue(single_input == self.wrpc.finalizepsbt(first_sign_res['psbt'])['complete']) # Second may have nothing to sign (1 input case) # and also may throw an error(e.g., ColdCard) second_sign_res = self.do_command(self.dev_args + ['signtx', second_psbt]) if 'psbt' in second_sign_res: self.assertTrue(not self.wrpc.finalizepsbt(second_sign_res['psbt'])['complete']) combined_psbt = self.wrpc.combinepsbt([first_sign_res['psbt'], second_sign_res['psbt']]) else: self.assertTrue('error' in second_sign_res) combined_psbt = first_sign_res['psbt'] finalize_res = self.wrpc.finalizepsbt(combined_psbt) self.assertTrue(finalize_res['complete']) self.assertTrue(self.wrpc.testmempoolaccept([finalize_res['hex']])[0]["allowed"]) return finalize_res['hex'] def _test_signtx(self, input_type, multisig, external): # Import some keys to the watch only wallet and send coins to them keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '30', '40']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '--internal', '30', '40']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) sh_wpkh_addr = self.wrpc.getnewaddress('', 'p2sh-segwit') wpkh_addr = self.wrpc.getnewaddress('', 'bech32') pkh_addr = self.wrpc.getnewaddress('', 'legacy') self.wrpc.importaddress(wpkh_addr) self.wrpc.importaddress(pkh_addr) # pubkeys to construct 2-of-3 multisig descriptors for import sh_wpkh_info = self.wrpc.getaddressinfo(sh_wpkh_addr) wpkh_info = self.wrpc.getaddressinfo(wpkh_addr) pkh_info = self.wrpc.getaddressinfo(pkh_addr) # Get origin info/key pair so wallet doesn't forget how to # sign with keys post-import pubkeys = [sh_wpkh_info['desc'][8:-11], wpkh_info['desc'][5:-10], pkh_info['desc'][4:-10]] # Get the descriptors with their checksums sh_multi_desc = self.wrpc.getdescriptorinfo('sh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + '))')['descriptor'] sh_wsh_multi_desc = self.wrpc.getdescriptorinfo('sh(wsh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + ')))')['descriptor'] wsh_multi_desc = self.wrpc.getdescriptorinfo('wsh(sortedmulti(2,' + pubkeys[2] + ',' + pubkeys[1] + ',' + pubkeys[0] + '))')['descriptor'] sh_multi_import = {'desc': sh_multi_desc, "timestamp": "now", "label": "shmulti"} sh_wsh_multi_import = {'desc': sh_wsh_multi_desc, "timestamp": "now", "label": "shwshmulti"} # re-order pubkeys to allow import without "already have private keys" error wsh_multi_import = {'desc': wsh_multi_desc, "timestamp": "now", "label": "wshmulti"} multi_result = self.wrpc.importmulti([sh_multi_import, sh_wsh_multi_import, wsh_multi_import]) self.assertTrue(multi_result[0]['success']) self.assertTrue(multi_result[1]['success']) self.assertTrue(multi_result[2]['success']) sh_multi_addr = self.wrpc.getaddressesbylabel("shmulti").popitem()[0] sh_wsh_multi_addr = self.wrpc.getaddressesbylabel("shwshmulti").popitem()[0] wsh_multi_addr = self.wrpc.getaddressesbylabel("wshmulti").popitem()[0] in_amt = 3 out_amt = in_amt // 3 number_inputs = 0 # Single-sig if input_type == 'segwit' or input_type == 'all': self.wpk_rpc.sendtoaddress(sh_wpkh_addr, in_amt) self.wpk_rpc.sendtoaddress(wpkh_addr, in_amt) number_inputs += 2 if input_type == 'legacy' or input_type == 'all': self.wpk_rpc.sendtoaddress(pkh_addr, in_amt) number_inputs += 1 # Now do segwit/legacy multisig if multisig: if input_type == 'legacy' or input_type == 'all': self.wpk_rpc.sendtoaddress(sh_multi_addr, in_amt) number_inputs += 1 if input_type == 'segwit' or input_type == 'all': self.wpk_rpc.sendtoaddress(wsh_multi_addr, in_amt) self.wpk_rpc.sendtoaddress(sh_wsh_multi_addr, in_amt) number_inputs += 2 self.wpk_rpc.generatetoaddress(6, self.wpk_rpc.getnewaddress()) # Spend different amounts, requiring 1 to 3 inputs for i in range(number_inputs): # Create a psbt spending the above if i == number_inputs - 1: self.assertTrue((i + 1) * in_amt == self.wrpc.getbalance("*", 0, True)) psbt = self.wrpc.walletcreatefundedpsbt([], [{self.wpk_rpc.getnewaddress('', 'legacy'): (i + 1) * out_amt}, {self.wpk_rpc.getnewaddress('', 'p2sh-segwit'): (i + 1) * out_amt}, {self.wpk_rpc.getnewaddress('', 'bech32'): (i + 1) * out_amt}], 0, {'includeWatching': True, 'subtractFeeFromOutputs': [0, 1, 2]}, True) if external: # Sign with unknown inputs in two steps self._generate_and_finalize(True, psbt) # Sign all inputs all at once final_tx = self._generate_and_finalize(False, psbt) # Send off final tx to sweep the wallet self.wrpc.sendrawtransaction(final_tx) # Test wrapper to avoid mixed-inputs signing for Ledger def test_signtx(self): supports_mixed = {'coldcard', 'trezor_1', 'digitalbitbox', 'keepkey'} supports_multisig = {'ledger', 'trezor_1', 'digitalbitbox', 'keepkey', 'coldcard', 'trezor_t'} supports_external = {'ledger', 'trezor_1', 'digitalbitbox', 'keepkey', 'coldcard'} if self.full_type not in supports_mixed: self._test_signtx("legacy", self.full_type in supports_multisig, self.full_type in supports_external) self._test_signtx("segwit", self.full_type in supports_multisig, self.full_type in supports_external) else: self._test_signtx("all", self.full_type in supports_multisig, self.full_type in supports_external) # Make a huge transaction which might cause some problems with different interfaces def test_big_tx(self): # make a huge transaction that is unrelated to the hardware wallet outputs = [] num_inputs = 60 for i in range(0, num_inputs): outputs.append({self.wpk_rpc.getnewaddress('', 'legacy'): 0.001}) psbt = self.wpk_rpc.walletcreatefundedpsbt([], outputs, 0, {}, True)['psbt'] psbt = self.wpk_rpc.walletprocesspsbt(psbt)['psbt'] tx = self.wpk_rpc.finalizepsbt(psbt)['hex'] txid = self.wpk_rpc.sendrawtransaction(tx) inputs = [] for i in range(0, num_inputs): inputs.append({'txid': txid, 'vout': i}) psbt = self.wpk_rpc.walletcreatefundedpsbt(inputs, [{self.wpk_rpc.getnewaddress('', 'legacy'): 0.001 * num_inputs}], 0, {'subtractFeeFromOutputs': [0]}, True)['psbt'] # For cli, this should throw an exception try: result = self.do_command(self.dev_args + ['signtx', psbt]) if self.interface == 'cli': self.fail('Big tx did not cause CLI to error') if self.type == 'coldcard': self.assertEqual(result['code'], -7) else: self.assertNotIn('code', result) self.assertNotIn('error', result) except OSError: if self.interface == 'cli': pass
class Controller: def __init__(self, settings, nfc_broadcast): self.nfc_broadcast = nfc_broadcast self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.single_screen_mode = settings['single_screen_mode'] self.green_addresses = settings['green_addresses'] self.bt_addr = None def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect( self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect( self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.single_screen_mode) if not self.single_screen_mode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.single_screen_mode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % ( cur_amount, currency, self.exchange_rate, currency, self.exchange_rate_source) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) btc_uri = self.create_btc_uri(self.current_address, amount_str, self.bt_addr) imgdata = self.create_img_data(btc_uri) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) self.nfc_broadcast.set_btc_uri(btc_uri) def create_btc_uri(self, address, amount_str, bt_addr): btc_uri = "bitcoin:%s?amount=%s" % (address, amount_str) if bt_addr != None: bt_addr_stripped = bt_addr.translate(None, ':') btc_uri += "&bt=%s" % bt_addr_stripped return btc_uri def create_img_data(self, btc_uri): (_, size, img) = qrencode.encode(btc_uri) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid): if not hasattr(self, 'app'): return # not yet read # emit signal, so we can process this on the Qt GUI thread self.app.emit( QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), txid) def _new_transaction_received(self, txid): # check if we are waiting for a payment if self.current_address == "": return # check if the txid looks sane before passing it # to bitcoind (for security reasons; might be overly # paranoid, but can't hurt) if re.search("^[a-f0-9]*$", txid) == None: return tx_info = self.bitcoind.gettransaction(txid) output_addresses = [] for detail in tx_info['details']: output_addresses.append(detail['address']) if self.current_address not in output_addresses: return msg = "Transaction to %s received." % self.current_address (from_green_address, green_address_msg) = self.green_address_check(txid) if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def bluetooth_available(self, bt_addr): self.bt_addr = bt_addr def new_transaction_via_bluetooth(self, tx): try: self.bitcoind.sendrawtransaction(tx) except JSONRPCException: # ignore, if this did not work - we might # have already received the transaction in # a different way pass def green_address_check(self, txid): found = False msg = "" origins = self.get_origins(txid) for origin in origins: if origin in self.green_addresses: found = True msg = self.green_addresses[origin] break return (found, msg) def get_origins(self, txid): try: origins = [] raw_tx = self.bitcoind.getrawtransaction(txid, 1) vins = raw_tx['vin'] for vin in vins: raw_tx = self.bitcoind.getrawtransaction(vin['txid'], 1) for vout in raw_tx['vout']: if vin['vout'] == vout['n']: origins.extend(vout['scriptPubKey']['addresses']) return origins except JSONRPCException: return [] def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') self.merchant_gui.update_status("System ready.") # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): if not hasattr(self, 'app'): return # not yet read self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
class myrstat(object): plotpath = None rpc = None block_window = None block_domain = None algos = [] diffs = [] heights = [] versions = [] bip9bits = [] sizes = [] times = [] txnums = [] blocklist = [] algolist = ['sha256d', 'scrypt', 'groestl', 'yescrypt', 'argon2d'] colorlist = ['#8ecf1d', '#2db6db', '#d7370c', '#ffe21b', '#8f3c85'] figsize = (8, 6) lw = 2.0 def __init__(self): # load config: Config = ConfigParser.ConfigParser(allow_no_value=True) Config.read('config.cfg') network = Config.get('Global', 'network') rpcusername = Config.get(network, 'rpcusername') rpcpassword = Config.get(network, 'rpcpassword') rpcport = Config.getint(network, 'rpcport') rpchostip = Config.get(network, 'rpchostip') self.plotpath = str(Config.get(network, 'htmlpath')) self.block_window = Config.getint(network, 'block_window') self.block_domain = Config.getint(network, 'block_domain') # set rpc: self.rpc = AuthServiceProxy('http://' + rpcusername + ':' + rpcpassword + '@' + rpchostip + ':' + str(rpcport)) def run(self): self.getdata() self.getblockwindowlist() self.plotalgos() self.plotalgodiffs() self.plotversionma_algo() self.plotversionma() def get_moving_average_for_algo(self, algo, dlist, value, bip9=False): """Given an algo, give moving average percentage of value. Here bip9 is actually the bit signaling + 1""" # first get data for the algo: da = self.get_data_for_algo(algo, dlist, start=0) h = self.get_data_for_algo(algo, self.heights, start=0) pct = [] # loop through data to get list that match targets: for i, ds in enumerate(da): (x, y) = self.get_data_for_window(h, da, h[i] - self.block_window, h[i]) domain_length = len(x) c = 0 for tmp in y: if bip9: # in this case, value is the bit we are seeking tmp = bin(tmp) if (tmp[0] != '-') and (len(tmp) >= value + 2): if tmp[-value] == '1': c += 1 else: if tmp == value: c += 1 if (domain_length == 0): pct.append(0.) else: pct.append(float(c) / float(domain_length) * 100.) return pct def get_moving_average(self, dlist, value, bip9=False): """Give moving average percentage of value. Here bip9 is actually the bit signaling + 1""" # first get data for the algo: da = dlist h = self.heights pct = [] # loop through data to get list that match targets: for i, ds in enumerate(da): (x, y) = self.get_data_for_window(h, da, h[i] - self.block_window, h[i]) domain_length = len(x) c = 0 for tmp in y: if bip9: # in this case, value is the bit we are seeking tmp = bin(tmp) if (tmp[0] != '-') and (len(tmp) >= value + 2): if tmp[-value] == '1': c += 1 else: if tmp == value: c += 1 if (domain_length == 0): pct.append(0.) else: pct.append(float(c) / float(domain_length) * 100.) return pct def getdata(self): """Get data via rpc.""" info = self.rpc.getblockchaininfo() block_height = info['blocks'] block_min = block_height - self.block_domain - self.block_window for bh in xrange(block_min, block_height + 1, 1): bhash = self.rpc.getblockhash(bh) block = self.rpc.getblock(bhash) self.algos.append(block['pow_algo']) self.diffs.append(block['difficulty']) self.heights.append(block['height']) self.versions.append((block['version'] & 255)) self.sizes.append(block['size']) self.times.append(block['time']) self.txnums.append(len(block['tx'])) if ((block['version'] & 0xFF000000) == 536870912): self.bip9bits.append((block['version'] & 0x000000FF)) else: self.bip9bits.append(-1) def get_data_for_window(self, x, y, xmin, xmax): """Return a list of y values in x that are between xmin and xmax.""" yi = [] xi = [] for i, tmp in enumerate(x): if (tmp >= xmin) and (tmp <= xmax): yi.append(y[i]) xi.append(x[i]) return (xi, yi) def moving_average_pct(self, raw, tgt): """generate a subset of data based on looking back by block_window.""" d = [] for i in xrange(self.block_window, self.block_domain + self.block_window + 1): count = 0. for k in xrange(0, self.block_window): if (raw[i - k] == tgt): count += 1 d.append(count / self.block_window * 100.) return d def getblockwindowlist(self): """Gets the block list for plotting.""" for i in xrange(self.block_window, self.block_domain + self.block_window + 1): self.blocklist.append(self.heights[i]) def get_data_for_algo(self, algo, dlist, start=0): """Get the list corresponding to the algo.""" d = [] for i in xrange(start, self.block_domain + self.block_window + 1): if (self.algos[i] == algo): d.append(dlist[i]) return d def plotalgos(self): """plots aglos""" figure(figsize=self.figsize) for i, algo in enumerate(self.algolist): d = self.moving_average_pct(self.algos, algo) plt.plot(self.blocklist, d, '-', color=self.colorlist[i], label=algo, linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.get_yaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlabel('Block Number') ax.set_ylabel('% of Blocks') ax.set_title('Block % for Mining Algorithms') legend(self.algolist, loc=0, prop={'size': 8}) savefig(self.plotpath + 'algohist.png', bbox_inches='tight') def plotversionma(self): """plots block versions""" figure(figsize=self.figsize) dm1 = self.get_moving_average(self.bip9bits, -1) d1 = self.get_moving_average(self.bip9bits, 1, bip9=True) d2 = self.get_moving_average(self.bip9bits, 2, bip9=True) d3 = self.get_moving_average(self.bip9bits, 3, bip9=True) d5 = self.get_moving_average(self.bip9bits, 5, bip9=True) d6 = self.get_moving_average(self.bip9bits, 6, bip9=True) d7 = self.get_moving_average(self.bip9bits, 7, bip9=True) # create an indicator line: di = [] dibip = [] for i in d1: di.append(75.) h = self.heights # block window: bw_calc = h[-1] - (h[-1] % self.block_window) bw_x = [ bw_calc, bw_calc, bw_calc - self.block_window, bw_calc - self.block_window, bw_calc ] bw_y = [0, 100, 100, 0, 0] #plt.plot(h,dm1,'-',color='red',label='Legacy Blocks', # linewidth=self.lw) #plt.plot(h,d1,'-',color='blue',label='CSV Blocks', # linewidth=self.lw) #plt.plot(h,d2,'-',color='magenta',label='segwit Blocks', # linewidth=self.lw) plt.plot(h, d3, '-', color='cyan', label='legbit Blocks', linewidth=self.lw) #plt.plot(h,d5,'-',color='red',label='reservealgo Blocks', # linewidth=self.lw) #plt.plot(h,d6,'-',color='magenta',label='longblocks Blocks', # linewidth=self.lw) plt.plot(h, d7, '-', color='blue', label='argon2d Blocks', linewidth=self.lw) plt.plot(h, di, '-.', color='green', label='BIP9 Activation Threshold', linewidth=self.lw) plt.plot(bw_x, bw_y, '-.', color='orange', label='Block Window', linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.set_ylim([-10., 110.]) ax.set_ylabel('%') legend(loc=0, prop={'size': 8}) ax.set_title('Block Softforks') ax.set_xlabel('Block Number') savefig(self.plotpath + 'versionma.png', bbox_inches='tight') def plotalgodiffs(self): figure(figsize=self.figsize) for i, algo in enumerate(self.algolist): d = self.get_data_for_algo(algo, self.diffs) h = self.get_data_for_algo(algo, self.heights) plt.subplot(len(self.algolist), 1, i + 1) plt.plot(h, d, '-', color=self.colorlist[i], label=algo, linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.set_ylabel(algo) if i == 0: ax.set_title('Mining Difficulty') if not i == (len(self.algolist) - 1): ax.xaxis.set_ticklabels([]) else: ax.set_xlabel('Block Number') savefig(self.plotpath + 'diffhist.png', bbox_inches='tight') def plotversionma_algo(self): figure(figsize=self.figsize) for i, algo in enumerate(self.algolist): dm1 = self.get_moving_average_for_algo(algo, self.bip9bits, -1) d1 = self.get_moving_average_for_algo(algo, self.bip9bits, 1, bip9=True) d2 = self.get_moving_average_for_algo(algo, self.bip9bits, 2, bip9=True) d3 = self.get_moving_average_for_algo(algo, self.bip9bits, 3, bip9=True) d5 = self.get_moving_average_for_algo(algo, self.bip9bits, 5, bip9=True) d6 = self.get_moving_average_for_algo(algo, self.bip9bits, 6, bip9=True) d7 = self.get_moving_average_for_algo(algo, self.bip9bits, 7, bip9=True) h = self.get_data_for_algo(algo, self.heights) plt.subplot(len(self.algolist), 1, i + 1) #plt.plot(h,dm1,'-',color='red',label='Legacy Blocks', # linewidth=self.lw) #plt.plot(h,d1,'-',color='blue',label='CSV Blocks', # linewidth=self.lw) #plt.plot(h,d2,'-',color='magenta',label='segwit Blocks', # linewidth=self.lw) plt.plot(h, d3, '-', color='cyan', label='legbit Blocks', linewidth=self.lw) #plt.plot(h,d5,'-',color='red',label='reservealgo Blocks', # linewidth=self.lw) #plt.plot(h,d6,'-',color='magenta',label='longblocks Blocks', # linewidth=self.lw) plt.plot(h, d7, '-', color='blue', label='argon2d Blocks', linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.set_ylim([-10., 110.]) ax.set_ylabel(algo) legend(loc=3, prop={'size': 8}) if i == 0: ax.set_title('Block Softfork %') if not i == (len(self.algolist) - 1): ax.xaxis.set_ticklabels([]) else: ax.set_xlabel('Block Number') savefig(self.plotpath + 'algoversionma.png', bbox_inches='tight')
class myrstat(object): plotpath = None rpc = None block_window = None block_domain = None algos = [] diffs = [] heights = [] versions = [] bip9bits = [] sizes = [] times = [] txnums = [] blocklist = [] algolist = ['NeoScrypt','Argon2d','Lyra2CZ'] colorlist = ['#8ecf1d','#2db6db','#a654c9'] figsize=(8,6) lw=2.0 def __init__(self): # load config: Config = ConfigParser.ConfigParser(allow_no_value = True) Config.read('config.cfg') network = Config.get('Global','network') rpcusername = Config.get(network,'rpcusername') rpcpassword = Config.get(network,'rpcpassword') rpcport = Config.getint(network,'rpcport') rpchostip = Config.get(network,'rpchostip') self.plotpath = str(Config.get(network,'htmlpath')) self.block_window = Config.getint(network,'block_window') self.block_domain = Config.getint(network,'block_domain') # set rpc: self.rpc = AuthServiceProxy('http://'+rpcusername+':'+rpcpassword+'@'+ rpchostip+':'+str(rpcport)) def run(self): self.getdata() self.getblockwindowlist() self.plotalgos() self.plotalgodiffs() # self.plotversionma_algo() # self.plotversionma() def get_moving_average_for_algo(self,algo,dlist,value,bip9=False): """Given an algo, give moving average percentage of value. Here bip9 is actually the bit signaling + 1""" # first get data for the algo: da = self.get_data_for_algo(algo,dlist,start=0) h = self.get_data_for_algo(algo,self.heights,start=0) pct = [] # loop through data to get list that match targets: for i,ds in enumerate(da): (x,y) = self.get_data_for_window(h,da,h[i]-self.block_window,h[i]) domain_length=len(x) c=0 for tmp in y: if bip9: # in this case, value is the bit we are seeking tmp = bin(tmp) if (tmp[0]!='-') and (len(tmp)>=value+2): if tmp[-value]=='1': c+=1 else: if tmp==value: c+=1 if (domain_length==0): pct.append(0.) else: pct.append(float(c)/float(domain_length)*100.) return pct def get_moving_average(self,dlist,value,bip9=False): """Give moving average percentage of value. Here bip9 is actually the bit signaling + 1""" # first get data for the algo: da = dlist h = self.heights pct = [] # loop through data to get list that match targets: for i,ds in enumerate(da): (x,y) = self.get_data_for_window(h,da,h[i]-self.block_window,h[i]) domain_length=len(x) c=0 for tmp in y: if bip9: # in this case, value is the bit we are seeking tmp = bin(tmp) if (tmp[0]!='-') and (len(tmp)>=value+2): if tmp[-value]=='1': c+=1 else: if tmp==value: c+=1 if (domain_length==0): pct.append(0.) else: pct.append(float(c)/float(domain_length)*100.) return pct def getdata(self): """Get data via rpc.""" info = self.rpc.getblockchaininfo() block_height = info['blocks'] block_min = block_height - self.block_domain - self.block_window for bh in xrange(block_min,block_height+1,1): bhash = self.rpc.getblockhash(bh) block = self.rpc.getblock(bhash) self.algos.append(block['pow_algo']) self.diffs.append(block['difficulty']) self.heights.append(block['height']) self.versions.append((block['version'] & 255)) self.sizes.append(block['size']) self.times.append(block['time']) self.txnums.append(len(block['tx'])) if ((block['version'] & 0xFF000000) == 536870912): self.bip9bits.append((block['version'] & 0x000000FF)) else: self.bip9bits.append(-1) def get_data_for_window(self,x,y,xmin,xmax): """Return a list of y values in x that are between xmin and xmax.""" yi = [] xi = [] for i,tmp in enumerate(x): if (tmp>=xmin) and (tmp<=xmax): yi.append(y[i]) xi.append(x[i]) return (xi,yi) def moving_average_pct(self,raw,tgt): """generate a subset of data based on looking back by block_window.""" d = [] for i in xrange(self.block_window, self.block_domain+self.block_window+1): count=0. for k in xrange(0,self.block_window): if (raw[i-k]==tgt): count+=1 d.append(count/self.block_window*100.) return d def getblockwindowlist(self): """Gets the block list for plotting.""" for i in xrange(self.block_window, self.block_domain+self.block_window+1): self.blocklist.append(self.heights[i]) def get_data_for_algo(self,algo,dlist,start=0): """Get the list corresponding to the algo.""" d = [] for i in xrange(start,self.block_domain+self.block_window+1): if (self.algos[i]==algo): d.append(dlist[i]) return d def plotalgos(self): """plots aglos""" figure(figsize=self.figsize) for i, algo in enumerate(self.algolist): d = self.moving_average_pct(self.algos,algo) plt.plot(self.blocklist,d,'-',color=self.colorlist[i],label=algo, linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.get_yaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlabel('Block Number') ax.set_ylabel('% of Blocks') ax.set_title('Block % for Mining Algorithms') legend(self.algolist,loc=0,prop={'size':8}) savefig(self.plotpath+'algohist.png',bbox_inches='tight') def plotalgodiffs(self): figure(figsize=self.figsize) for i, algo in enumerate(self.algolist): d = self.get_data_for_algo(algo,self.diffs) h = self.get_data_for_algo(algo,self.heights) plt.subplot(len(self.algolist),1,i+1) plt.plot(h,d,'-',color=self.colorlist[i],label=algo, linewidth=self.lw) plt.hold('on') plt.grid('on') ax = plt.gca() ax.get_xaxis().set_minor_locator(ticker.AutoMinorLocator()) ax.grid(b=True, which='major', color='#a0a0a0', linestyle='-', linewidth=1.0) ax.grid(b=True, which='minor', color='#dcdcdc', linestyle='-', linewidth=0.5) ax.get_xaxis().get_major_formatter().set_scientific(False) ax.get_xaxis().get_major_formatter().set_useOffset(False) ax.set_xlim([self.blocklist[0], self.blocklist[-1]]) ax.set_ylabel(algo) if i==0: ax.set_title('Mining Difficulty') if not i==(len(self.algolist)-1): ax.xaxis.set_ticklabels([]) else: ax.set_xlabel('Block Number') savefig(self.plotpath+'diffhist.png',bbox_inches='tight')
args = parser.parse_args() args.config = os.path.expanduser(args.config) # Get user and password from config user = None password = None if os.path.isfile(args.config): with open(args.config, 'r', encoding='utf8') as f: for line in f: if line.startswith("rpcuser="******"=")[1].strip("\n") if line.startswith("rpcpassword="******"=")[1].strip("\n") else: raise FileNotFoundError("Missing lotus.conf") if user is None: raise ValueError("Config is missing rpcuser") if password is None: raise ValueError("Config is missing rpcpassword") args.rpc = AuthServiceProxy(service_url='http://{}:{}@{}'.format( user, password, args.address), timeout=1200) output = main(vars(args)) if output: print(output)
class TestDisplayAddress(DeviceTestCase): def setUp(self): self.rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format( self.rpc_userpass)) if '{}_test'.format(self.full_type) not in self.rpc.listwallets(): self.rpc.createwallet('{}_test'.format(self.full_type), True) self.wrpc = AuthServiceProxy( 'http://{}@127.0.0.1:18443/wallet/{}_test'.format( self.rpc_userpass, self.full_type)) self.wpk_rpc = AuthServiceProxy( 'http://{}@127.0.0.1:18443/wallet/'.format(self.rpc_userpass)) if '--testnet' not in self.dev_args: self.dev_args.append('--testnet') self.emulator.start() def test_display_address_bad_args(self): result = self.do_command(self.dev_args + [ 'displayaddress', '--sh_wpkh', '--wpkh', '--path', 'm/49h/1h/0h/0/0' ]) self.assertIn('error', result) self.assertIn('code', result) self.assertEqual(result['code'], -7) def test_display_address_path(self): result = self.do_command( self.dev_args + ['displayaddress', '--path', 'm/44h/1h/0h/0/0']) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) result = self.do_command( self.dev_args + ['displayaddress', '--sh_wpkh', '--path', 'm/49h/1h/0h/0/0']) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) result = self.do_command( self.dev_args + ['displayaddress', '--wpkh', '--path', 'm/84h/1h/0h/0/0']) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) def test_display_address_bad_path(self): result = self.do_command(self.dev_args + ['displayaddress', '--path', 'f']) self.assertEquals(result['code'], -7) def test_display_address_descriptor(self): account_xpub = self.do_command(self.dev_args + ['getxpub', 'm/84h/1h/0h'])['xpub'] p2sh_segwit_account_xpub = self.do_command( self.dev_args + ['getxpub', 'm/49h/1h/0h'])['xpub'] legacy_account_xpub = self.do_command( self.dev_args + ['getxpub', 'm/44h/1h/0h'])['xpub'] # Native SegWit address using xpub: result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h]' + account_xpub + '/0/0)' ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # Native SegWit address using hex encoded pubkey: result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h]' + xpub_to_pub_hex(account_xpub) + '/0/0)' ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # P2SH wrapped SegWit address using xpub: result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'sh(wpkh([' + self.fingerprint + '/49h/1h/0h]' + p2sh_segwit_account_xpub + '/0/0))' ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # Legacy address result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'pkh([' + self.fingerprint + '/44h/1h/0h]' + legacy_account_xpub + '/0/0)' ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # Should check xpub result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h]' + "not_and_xpub" + '/0/0)' ]) self.assertIn('error', result) self.assertIn('code', result) self.assertEqual(result['code'], -7) # Should check hex pub result = self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'wpkh([' + self.fingerprint + '/84h/1h/0h]' + "not_and_xpub" + '/0/0)' ]) self.assertIn('error', result) self.assertIn('code', result) self.assertEqual(result['code'], -7) # Should check fingerprint self.do_command(self.dev_args + [ 'displayaddress', '--desc', 'wpkh([00000000/84h/1h/0h]' + account_xpub + '/0/0)' ]) self.assertIn('error', result) self.assertIn('code', result) self.assertEqual(result['code'], -7) def test_display_address_multisig_path(self): supports_multisig = {'trezor_1', 'keepkey', 'coldcard', 'trezor_t'} if self.full_type not in supports_multisig: return # Import some keys to the watch only wallet and get multisig address keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '40', '50']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) keypool_desc = self.do_command( self.dev_args + ['getkeypool', '--sh_wpkh', '--internal', '40', '50']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) sh_wpkh_addr = self.wrpc.getnewaddress('', 'p2sh-segwit') wpkh_addr = self.wrpc.getnewaddress('', 'bech32') pkh_addr = self.wrpc.getnewaddress('', 'legacy') self.wrpc.importaddress(wpkh_addr) self.wrpc.importaddress(pkh_addr) # pubkeys to construct 2-of-3 multisig descriptors for import sh_wpkh_info = self.wrpc.getaddressinfo(sh_wpkh_addr) wpkh_info = self.wrpc.getaddressinfo(wpkh_addr) pkh_info = self.wrpc.getaddressinfo(pkh_addr) pubkeys = [ sh_wpkh_info['desc'][8:-11], wpkh_info['desc'][5:-10], pkh_info['desc'][4:-10] ] # Get the descriptors with their checksums sh_multi_desc = self.wrpc.getdescriptorinfo('sh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + '))')['descriptor'] sh_wsh_multi_desc = self.wrpc.getdescriptorinfo( 'sh(wsh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + ')))')['descriptor'] wsh_multi_desc = self.wrpc.getdescriptorinfo('wsh(sortedmulti(2,' + pubkeys[2] + ',' + pubkeys[1] + ',' + pubkeys[0] + '))')['descriptor'] sh_multi_import = { 'desc': sh_multi_desc, "timestamp": "now", "label": "shmulti-display" } sh_wsh_multi_import = { 'desc': sh_wsh_multi_desc, "timestamp": "now", "label": "shwshmulti-display" } # re-order pubkeys to allow import without "already have private keys" error wsh_multi_import = { 'desc': wsh_multi_desc, "timestamp": "now", "label": "wshmulti-display" } multi_result = self.wrpc.importmulti( [sh_multi_import, sh_wsh_multi_import, wsh_multi_import]) self.assertTrue(multi_result[0]['success']) self.assertTrue(multi_result[1]['success']) self.assertTrue(multi_result[2]['success']) sh_multi_addr = self.wrpc.getaddressesbylabel( "shmulti-display").popitem()[0] sh_wsh_multi_addr = self.wrpc.getaddressesbylabel( "shwshmulti-display").popitem()[0] wsh_multi_addr = self.wrpc.getaddressesbylabel( "wshmulti-display").popitem()[0] sh_multi_addr_redeem_script = self.wrpc.getaddressinfo( sh_multi_addr)['hex'] sh_wsh_multi_addr_redeem_script = self.wrpc.getaddressinfo( sh_multi_addr)['hex'] wsh_multi_addr_redeem_script = self.wrpc.getaddressinfo( sh_multi_addr)['hex'] path = pubkeys[2][1:24] + ',' + pubkeys[1][1:24] + ',' + pubkeys[0][ 1:24] # need to replace `'` with `h` for stdin option to work path = path.replace("'", "h") # legacy result = self.do_command(self.dev_args + [ 'displayaddress', '--path', path, '--redeem_script', sh_multi_addr_redeem_script ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) self.assertEqual(sh_multi_addr, result['address']) # wrapped segwit result = self.do_command(self.dev_args + [ 'displayaddress', '--sh_wpkh', '--path', path, '--redeem_script', sh_wsh_multi_addr_redeem_script ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) self.assertEqual(sh_wsh_multi_addr, result['address']) # native setwit result = self.do_command(self.dev_args + [ 'displayaddress', '--wpkh', '--path', path, '--redeem_script', wsh_multi_addr_redeem_script ]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # removes prefix and checksum since regtest gives # prefix `bcrt` on Bitcoin Core while wallets return testnet `tb` prefix self.assertEqual(wsh_multi_addr[4:58], result['address'][2:56]) def test_display_address_multisig_descriptor(self): supports_multisig = {'trezor_1', 'keepkey', 'coldcard', 'trezor_t'} if self.full_type not in supports_multisig: return # Import some keys to the watch only wallet and get multisig address keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '50', '60']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) keypool_desc = self.do_command( self.dev_args + ['getkeypool', '--sh_wpkh', '--internal', '50', '60']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) sh_wpkh_addr = self.wrpc.getnewaddress('', 'p2sh-segwit') wpkh_addr = self.wrpc.getnewaddress('', 'bech32') pkh_addr = self.wrpc.getnewaddress('', 'legacy') self.wrpc.importaddress(wpkh_addr) self.wrpc.importaddress(pkh_addr) # pubkeys to construct 2-of-3 multisig descriptors for import sh_wpkh_info = self.wrpc.getaddressinfo(sh_wpkh_addr) wpkh_info = self.wrpc.getaddressinfo(wpkh_addr) pkh_info = self.wrpc.getaddressinfo(pkh_addr) pubkeys = [ sh_wpkh_info['desc'][8:-11], wpkh_info['desc'][5:-10], pkh_info['desc'][4:-10] ] # Get the descriptors with their checksums sh_multi_desc = self.wrpc.getdescriptorinfo('sh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + '))')['descriptor'] sh_wsh_multi_desc = self.wrpc.getdescriptorinfo( 'sh(wsh(sortedmulti(2,' + pubkeys[0] + ',' + pubkeys[1] + ',' + pubkeys[2] + ')))')['descriptor'] wsh_multi_desc = self.wrpc.getdescriptorinfo('wsh(sortedmulti(2,' + pubkeys[2] + ',' + pubkeys[1] + ',' + pubkeys[0] + '))')['descriptor'] sh_multi_import = { 'desc': sh_multi_desc, "timestamp": "now", "label": "shmulti-display-desc" } sh_wsh_multi_import = { 'desc': sh_wsh_multi_desc, "timestamp": "now", "label": "shwshmulti-display-desc" } # re-order pubkeys to allow import without "already have private keys" error wsh_multi_import = { 'desc': wsh_multi_desc, "timestamp": "now", "label": "wshmulti-display-desc" } multi_result = self.wrpc.importmulti( [sh_multi_import, sh_wsh_multi_import, wsh_multi_import]) self.assertTrue(multi_result[0]['success']) self.assertTrue(multi_result[1]['success']) self.assertTrue(multi_result[2]['success']) sh_multi_addr = self.wrpc.getaddressesbylabel( "shmulti-display-desc").popitem()[0] sh_wsh_multi_addr = self.wrpc.getaddressesbylabel( "shwshmulti-display-desc").popitem()[0] wsh_multi_addr = self.wrpc.getaddressesbylabel( "wshmulti-display-desc").popitem()[0] # need to replace `'` with `h` and to remove checksome for the stdin option to work sh_multi_desc = sh_multi_desc.replace("'", "h").split('#')[0] sh_wsh_multi_desc = sh_wsh_multi_desc.replace("'", "h").split('#')[0] wsh_multi_desc = wsh_multi_desc.replace("'", "h").split('#')[0] # legacy result = self.do_command(self.dev_args + ['displayaddress', '--desc', sh_multi_desc]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) self.assertEqual(sh_multi_addr, result['address']) # wrapped segwit result = self.do_command( self.dev_args + ['displayaddress', '--desc', sh_wsh_multi_desc]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) self.assertEqual(sh_wsh_multi_addr, result['address']) # native setwit result = self.do_command(self.dev_args + ['displayaddress', '--desc', wsh_multi_desc]) self.assertNotIn('error', result) self.assertNotIn('code', result) self.assertIn('address', result) # removes prefix and checksum since regtest gives # prefix `bcrt` on Bitcoin Core while wallets return testnet `tb` prefix self.assertEqual(wsh_multi_addr[4:58], result['address'][2:56])
class ApiReceiveApplication(tornado.web.Application): def __init__(self, options, instance_name): self.options = options self.instance_name = instance_name handlers = [ (r"/api/receive", ReceiveHandler), (r"/api/walletnotify/(?P<txid>[^\/]+)", WalletNotifyHandler), (r"/api/blocknotify/(?P<hash>[^\/]+)", BlockNotifyHandler), ] settings = dict(cookie_secret='cookie_secret') tornado.web.Application.__init__(self, handlers, **settings) input_log_file_handler = logging.handlers.TimedRotatingFileHandler( self.options.log, when='MIDNIGHT') formatter = logging.Formatter('%(asctime)s - %(message)s') input_log_file_handler.setFormatter(formatter) self.bitcoind = AuthServiceProxy(self.options.rpc_url) self.paytxfee = self.bitcoind.getinfo()['paytxfee'] self.replay_logger = logging.getLogger(self.instance_name) self.replay_logger.setLevel(logging.DEBUG) self.replay_logger.addHandler(input_log_file_handler) self.replay_logger.info('START') ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.DEBUG) ch.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) self.replay_logger.addHandler(ch) from models import Base, db_bootstrap engine = create_engine(self.options.db_engine, echo=self.options.db_echo) Base.metadata.create_all(engine) self.db_session = scoped_session(sessionmaker(bind=engine)) db_bootstrap(self.db_session) self.log_start_data() def invoke_callback_url(self, forwarding_address): url = forwarding_address.get_callback_url() self.log('EXECUTE', 'curl ' + url) context = ssl._create_unverified_context() http_client = httpclient.AsyncHTTPClient(defaults=dict( ssl_options=context)) http_client.fetch( url, partial(self.on_handle_callback_url, forwarding_address.id)) def on_handle_callback_url(self, forwarding_address_id, response): from models import ForwardingAddress forwarding_address = ForwardingAddress.get_by_id( self.db_session, forwarding_address_id) if response.error: self.log('ERROR', str(response.error)) forwarding_address.callback_number_of_errors += 1 self.db_session.add(forwarding_address) self.db_session.commit() else: if response.body == '*ok*': forwarding_address.is_confirmed_by_client = True self.db_session.add(forwarding_address) self.db_session.commit() def log(self, command, key, value=None): #if len(logging.getLogger().handlers): # logging.getLogger().handlers = [] # workaround to avoid stdout logging from the root logger log_msg = command + ',' + key if value: try: log_msg += ',' + value except Exception as e: try: log_msg += ',' + str(value) except Exception as e: try: log_msg += ',' + str(value) except Exception as e: log_msg += ', [object]' self.replay_logger.info(log_msg) def log_start_data(self): self.log('PARAM', 'BEGIN') self.log('PARAM', 'port', self.options.port) self.log('PARAM', 'log', self.options.log) self.log('PARAM', 'db_echo', self.options.db_echo) self.log('PARAM', 'db_engine', self.options.db_engine) self.log('PARAM', 'rpc_url', self.options.rpc_url) self.log('PARAM', 'END') from models import ForwardingAddress fwd_address_list = self.db_session.query(ForwardingAddress) for fwd_address in fwd_address_list: self.log('DB_ENTITY', 'FORWARDING_ADDRESS', fwd_address) bitcoin_info = self.bitcoind.getinfo() self.log('INFO', 'BITCOIND_GETINFO', str(bitcoin_info)) def clean_up(self): pass
sys.path.append(auth) from authproxy import AuthServiceProxy parser = argparse.ArgumentParser() parser.add_argument('datadir') parser.add_argument('txcount', type=int) args = parser.parse_args() # Wait for cookie file to be created while not os.path.exists(args.datadir + '/regtest/.cookie'): time.sleep(0.5) # Read .cookie file to get user and pass with open(args.datadir + '/regtest/.cookie') as f: userpass = f.readline().lstrip().rstrip() rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(userpass)) # Wait for bitcoind to be ready ready: bool = False while not ready: try: rpc.getblockchaininfo() ready = True except Exception: time.sleep(0.5) pass print('bitcoind ready') for item in rpc.listwalletdir()['wallets']: if 'big' == item['name']: break
def setup_wallets(self): wallet_name = '{}_{}_test'.format(self.full_type, self.id()) self.rpc.createwallet(wallet_name=wallet_name, disable_private_keys=True, descriptors=True) self.wrpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/{}'.format(self.rpc_userpass, wallet_name)) self.wpk_rpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/supply'.format(self.rpc_userpass))
def initialize_chain(test_dir): """ Create (or copy from cache) a 200-block-long chain and 4 wallets. bitcoind and bitcoin-cli must be in search path. """ # Due to the consensus change fix for the timejacking attack, we need to # ensure that the cache is pretty fresh. Specifically, we need the median # time past of the chain tip of the cache to be no more than 90 minutes # behind the current local time, or else mined blocks will be rejected by # all nodes, halting the test. With Sapling active by default, this requires # the chain tip itself to be no more than 75 minutes behind the current # local time. # # We address this here, by regenerating the cache if it is more than 60 # minutes old. This gives 15 minutes of slack initially that an RPC test has # to complete in, if it is started right at the oldest cache time. Within an # individual test, the first five calls to `generate` will each advance the # median time past of the chain tip by 2.5 minutes (with Sapling active by # default). Therefore, if the logic between the completion of any two # adjacent calls to `generate` within a test takes longer than 2.5 minutes, # the excess will subtract from the slack. if os.path.isdir(os.path.join("cache", "node0")): if os.stat("cache").st_mtime + (60 * 60) < time.time(): print("initialize_chain(): Removing stale cache") shutil.rmtree("cache") if not os.path.isdir(os.path.join("cache", "node0")): devnull = open("/dev/null", "w+") # Create cache directories, run bitcoinds: for i in range(4): datadir = initialize_datadir("cache", i) args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir=" + datadir, "-discover=0" ] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) bitcoind_processes[i] = subprocess.Popen(args) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: bitcoind started, calling bitcoin-cli -rpcwait getblockcount" subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir=" + datadir, "-rpcwait", "getblockcount" ], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "initialize_chain: bitcoin-cli -rpcwait getblockcount completed" devnull.close() rpcs = [] for i in range(4): try: url = "http://*****:*****@127.0.0.1:%d" % (rpc_port(i), ) rpcs.append(AuthServiceProxy(url)) except: sys.stderr.write("Error connecting to " + url + "\n") sys.exit(1) # Create a 200-block-long chain; each of the 4 nodes # gets 25 mature blocks and 25 immature. # Blocks are created with timestamps 2.5 minutes apart (matching the # chain defaulting above to Sapling active), starting 200 * 2.5 minutes # before the current time. block_time = int( time.time()) - (200 * PRE_BUTTERCUP_BLOCK_TARGET_SPACING) for i in range(2): for peer in range(4): for j in range(25): #set_node_times(rpcs, block_time) rpcs[peer].generate(1) block_time += PRE_BUTTERCUP_BLOCK_TARGET_SPACING # Must sync before next peer starts generating blocks sync_blocks(rpcs) # Check that local time isn't going backwards assert_greater_than(time.time() + 1, block_time) # Shut them down, and clean up cache directories: stop_nodes(rpcs) wait_bitcoinds() for i in range(4): os.remove(log_filename("cache", i, "debug.log")) os.remove(log_filename("cache", i, "db.log")) os.remove(log_filename("cache", i, "peers.dat")) os.remove(log_filename("cache", i, "fee_estimates.dat")) for i in range(4): from_dir = os.path.join("cache", "node" + str(i)) to_dir = os.path.join(test_dir, "node" + str(i)) shutil.copytree(from_dir, to_dir) initialize_datadir(test_dir, i) # Overwrite port/rpcport in zcash.conf
class Watcher(Process): def __init__(self, url, userpass, rpccookiefile, *args, **kwargs): super().__init__(*args, **kwargs) self.buf = b"" self.id = 0 self.userpass = userpass self.signals = None self.last_log_time = time.time() # Parse the URL self.purl = urlparse(url) if self.purl.scheme != "stratum+tcp": raise ValueError( f"Unrecognized scheme {self.purl.scheme}, only 'stratum+tcp' is allowed" ) if self.purl.hostname is None: raise ValueError(f"No hostname provided") if self.purl.port is None: raise ValueError(f"No port provided") if self.purl.path != "": raise ValueError( f"URL has a path {self.purl.path}, this is not valid") # Get the RPC cookie cookie_filepath = os.path.abspath(os.path.expanduser(rpccookiefile)) with open(cookie_filepath, "r") as f: self.rpc_userpass = f.readline() # Open RPC connection self.rpc = AuthServiceProxy( f"http://{self.rpc_userpass}@localhost:8332") self.last_seen_blockhash = self.rpc.getbestblockhash() self.init_socket() def init_socket(self): # Make the socket self.sock = socket.socket() self.sock.settimeout(600) def close(self): try: self.sock.shutdown(socket.SHUT_RDWR) except OSError: pass self.sock.close() LOG.debug(f"Disconnected from {urlunparse(self.purl)}") def get_msg(self): while True: split_buf = self.buf.split(b"\n", maxsplit=1) r = split_buf[0] try: resp = json.loads(r) # Remove r from the buffer if len(split_buf) == 2: self.buf = split_buf[1] else: self.buf = b"" # Decoded, so return this message return resp except json.JSONDecodeError: # Failed to decode, maybe missing, so try to get more new_buf = self.sock.recv(4096) if len(new_buf) == 0: raise EOFError("Socket EOF received") self.buf += new_buf def send_jsonrpc(self, method, params): # Build the jsonrpc request data = { "jsonrpc": "2.0", "id": self.id, "method": method, "params": params, } self.id += 1 # Send the jsonrpc request LOG.debug(f"Sending: {data}") json_data = json.dumps(data) + "\n" self.sock.send(json_data.encode()) # Get the jsonrpc reqponse resp = self.get_msg() LOG.debug(f"Received: {resp}") def get_stratum_work(self): # Open TCP connection to the server self.sock.connect((self.purl.hostname, self.purl.port)) LOG.debug(f"Connected to server {urlunparse(self.purl)}") # Subscribe to mining notifications self.send_jsonrpc("mining.subscribe", ["StratumWatcher/0.1"]) LOG.debug(f"Subscribed to pool notifications") # Authorize with the pool self.send_jsonrpc("mining.authorize", self.userpass.split(":")) LOG.debug(f"Authed with the pool") # Wait for notifications while True: try: n = self.get_msg() except Exception as e: LOG.debug(f"Received exception for {self.purl.hostname}: {e}") break LOG.debug(f"Received notification: {n}") # Check the notification for mining.notify if "method" in n and n["method"] == "mining.notify": # Get the previous block hash prev_bh_stratum = struct.unpack("<IIIIIIII", bytes.fromhex(n["params"][1])) prev_bh = struct.pack( "<IIIIIIII", prev_bh_stratum[7], prev_bh_stratum[6], prev_bh_stratum[5], prev_bh_stratum[4], prev_bh_stratum[3], prev_bh_stratum[2], prev_bh_stratum[1], prev_bh_stratum[0], ).hex() # Check that this is Bitcoin if prev_bh != self.last_seen_blockhash: # If the blockhash doesn't match what we've cached, ask bitcoind try: self.rpc.getblockheader(prev_bh) self.last_seen_blockhash = prev_bh except JSONRPCException: LOG.debug(f"Received non-Bitcoin work, ignoring") continue # Check for taproot versionbits block_ver_hex = n["params"][5] block_ver = int.from_bytes(bytes.fromhex(block_ver_hex), byteorder="big") if block_ver & (1 << 2): if self.signals is None: LOG.info( f"✅ Signaling initially: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() elif not self.signals: LOG.info( f"✅ Now signaling: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() elif time.time() - self.last_log_time > 300: LOG.info( f"✅ Still signaling: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() LOG.debug( f"Issued new work that SIGNALS ✅ for Taproot from {self.purl.hostname}:{self.purl.port}" ) self.signals = True else: if self.signals is None: LOG.info( f"❌ Not signaling initially: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() elif self.signals: LOG.info( f"❌ Stopped signaling: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() elif time.time() - self.last_log_time > 300: LOG.info( f"❌ Still not signaling: {self.purl.hostname}:{self.purl.port}" ) self.last_log_time = time.time() LOG.debug( f"Issued new work that DOES NOT SIGNAL ❌ for Taproot from {self.purl.hostname}:{self.purl.port}" ) self.signals = False def run(self): # If there is a socket exception, retry while True: try: self.get_stratum_work() except (ConnectionRefusedError, EOFError, socket.timeout): pass self.close() self.init_socket()
import random import urllib.request import json import re import sys # Requires running Core RPC server on standard mainnet RPC port if len(sys.argv) < 7: raise Exception( 'feeloop.py <RPC username> <RPC password> <oauth1> <oauth2> <token1> <token2>' ) while True: bitcoin_req = "http://" + sys.argv[1] + ":" + sys.argv[ 2] + "@127.0.0.1:8332" bitcoin = AuthServiceProxy(bitcoin_req) def get_rounded_feerate(result): rate = str(int(result * 1000000) / 10.0) + " sat/byte " if len(re.split("\.", rate)[0]) == 1: rate = " " + rate return rate try: mempool_info = bitcoin.getmempoolinfo() nextblock = [ "Next: ", bitcoin.estimatesmartfee(1, "ECONOMICAL")["feerate"] ] hour = ["1h: ", bitcoin.estimatesmartfee(6, "ECONOMICAL")["feerate"]] six_hours = [
def get_wallet_rpc(self, wallet): url = self.rpc_url + f"/wallet/{wallet}" return AuthServiceProxy(url)
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): """ Start a komodod and return RPC connection to it """ datadir = os.path.join(dirname, "node" + str(i)) # creating special config in case of cryptocondition asset chain test if extra_args[0] == '-ac_name=REGTEST': configpath = datadir + "/REGTEST.conf" with open(configpath, "w+") as config: config.write("regtest=1\n") config.write("rpcuser=rt\n") config.write("rpcpassword=rt\n") port = extra_args[3] config.write("rpcport=" + (port[9:]) + "\n") config.write("server=1\n") config.write("txindex=1\n") config.write("rpcworkqueue=256\n") config.write("rpcallowip=127.0.0.1\n") config.write("bind=127.0.0.1\n") config.write("rpcbind=127.0.0.1") if binary is None: binary = os.getenv("BITCOIND", "komodod") args = [ binary, "-datadir=" + datadir, "-keypool=1", "-discover=0", "-rest" ] if extra_args is not None: args.extend(extra_args) #print("args=" + ' '.join(args)) bitcoind_processes[i] = subprocess.Popen(args) devnull = open("/dev/null", "w+") cmd = os.getenv("BITCOINCLI", "komodo-cli") print("cmd=" + cmd) cmd_args = ' '.join(extra_args) + " -rpcwait getblockcount " if os.getenv("PYTHON_DEBUG", ""): print "start_node: komodod started, calling : " + cmd + " " + cmd_args strcmd = cmd + " " + cmd_args print("Running " + strcmd) import time time.sleep(2) subprocess.check_call(strcmd, shell=True) #subprocess.check_call([ os.getenv("BITCOINCLI", "komodo-cli"), "-datadir="+datadir] + # _rpchost_to_args(rpchost) + # ["-rpcwait", "-rpcport=6438", "getblockcount"], stdout=devnull) if os.getenv("PYTHON_DEBUG", ""): print "start_node: calling komodo-cli -rpcwait getblockcount returned" devnull.close() port = extra_args[3] url = "http://*****:*****@%s:%d" % (rpchost or '127.0.0.1', int(port[9:])) print("connecting to " + url) if timewait is not None: proxy = AuthServiceProxy(url, timeout=timewait) else: proxy = AuthServiceProxy(url) print("created proxy") proxy.url = url # store URL on proxy for info return proxy
def __init__(self, service_url, wallet_unlock_timeout): self.wallet_unlock_timeout = wallet_unlock_timeout self.api = AuthServiceProxy(service_url=service_url, verify=False)
class TestGetKeypool(DeviceTestCase): def setUp(self): self.rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(self.rpc_userpass)) if '{}_test'.format(self.full_type) not in self.rpc.listwallets(): self.rpc.createwallet('{}_test'.format(self.full_type), True) self.wrpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/{}_test'.format(self.rpc_userpass, self.full_type)) self.wpk_rpc = AuthServiceProxy('http://{}@127.0.0.1:18443/wallet/'.format(self.rpc_userpass)) if '--testnet' not in self.dev_args: self.dev_args.append('--testnet') self.emulator.start() def tearDown(self): self.emulator.stop() def test_getkeypool_bad_args(self): result = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '--wpkh', '0', '20']) self.assertIn('error', result) self.assertIn('code', result) self.assertEqual(result['code'], -7) def test_getkeypool(self): non_keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--nokeypool', '0', '20']) import_result = self.wpk_rpc.importmulti(non_keypool_desc) self.assertTrue(import_result[0]['success']) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '0', '20']) import_result = self.wpk_rpc.importmulti(keypool_desc) self.assertFalse(import_result[0]['success']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/44'/1'/0'/0/{}".format(i)) addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress()) self.assertEqual(addr_info['hdkeypath'], "m/44'/1'/0'/1/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '0', '20']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/0'/0/{}".format(i)) addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress()) self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/0'/1/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--wpkh', '0', '20']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/84'/1'/0'/0/{}".format(i)) addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress()) self.assertEqual(addr_info['hdkeypath'], "m/84'/1'/0'/1/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '--account', '3', '0', '20']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/3'/0/{}".format(i)) addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress()) self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/3'/1/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--wpkh', '--account', '3', '0', '20']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/84'/1'/3'/0/{}".format(i)) addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress()) self.assertEqual(addr_info['hdkeypath'], "m/84'/1'/3'/1/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--path', 'm/0h/0h/4h/*', '0', '20']) import_result = self.wrpc.importmulti(keypool_desc) self.assertTrue(import_result[0]['success']) for i in range(0, 21): addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress()) self.assertEqual(addr_info['hdkeypath'], "m/0'/0'/4'/{}".format(i)) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--path', '/0h/0h/4h/*', '0', '20']) self.assertEqual(keypool_desc['error'], 'Path must start with m/') self.assertEqual(keypool_desc['code'], -7) keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--path', 'm/0h/0h/4h/', '0', '20']) self.assertEqual(keypool_desc['error'], 'Path must end with /*') self.assertEqual(keypool_desc['code'], -7)
"Default: ~/.bitcoin/bitcoin.conf") args = parser.parse_args() args.config = os.path.expanduser(args.config) # Get user and password from config user = None password = None if os.path.isfile(args.config): with open(args.config, 'r', encoding='utf8') as f: for line in f: if line.startswith("rpcuser="******"=")[1].strip("\n") if line.startswith("rpcpassword="******"=")[1].strip("\n") else: raise FileNotFoundError("Missing bitcoin.conf") if user is None: raise ValueError("Config is missing rpcuser") if password is None: raise ValueError("Config is missing rpcpassword") args.rpc = AuthServiceProxy('http://{}:{}@{}'.format( user, password, args.address)) output = main(vars(args)) if output: print(output)
def setUp(self): self.rpc = AuthServiceProxy('http://{}@127.0.0.1:18443'.format(self.rpc_userpass)) if '--testnet' not in self.dev_args: self.dev_args.append('--testnet') self.emulator.start()
class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.singleScreenMode = settings['single-screen-mode'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect( self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect( self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.singleScreenMode) if not self.singleScreenMode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.singleScreenMode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % ( cur_amount, currency, self.exchange_rate, self.exchange_rate_source, currency) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid, output_addresses, from_green_address, green_address_msg): # emit signal, so we can process this on the Qt GUI thread self.app.emit( QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), (txid, output_addresses, from_green_address, green_address_msg)) def _new_transaction_received(self, data): (_, output_addresses, from_green_address, green_address_msg) = data if self.current_address != "" and self.current_address in output_addresses: msg = "Transaction to %s received." % self.current_address if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script( 'show_payment_received()') self.current_address = "" def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)
import logging from typing import Tuple from authproxy import AuthServiceProxy from block import Block from mempool import Mempool from private import rpc_user, rpc_password logger = logging.getLogger(__name__) logging.getLogger("BitcoinRPC").setLevel(logging.INFO) rpc = AuthServiceProxy("http://%s:%[email protected]:8332" % (rpc_user, rpc_password)) def fetch_synced() -> Tuple[dict, Block, Block, Mempool]: """ Fetches various data from Bitcoin Core RPC. Will check that tip_height before and after the fetches match, if they don't a block was found between calls and try again. """ tip_height, _height_check = 0, 1 previous, tip, blocktemplate, mempool = None, None, None, None mempool_ok = False while not tip_height == _height_check and not mempool_ok: tip_height = rpc.getblockcount() tip_hash = rpc.getbestblockhash() # First get blocktemplate as it's a subset of mempool blocktemplate = Block.from_blocktemplate(
class Controller: def __init__(self, settings): self.bitcoind = AuthServiceProxy(settings['rpc_url']) self.current_address = "" self.exchange_rate = 0.0 self.exchange_rate_source = "" self.currency = settings['exchange_rate_ticker']['currency'] self.singleScreenMode = settings['single-screen-mode'] def run(self): self.app = QtGui.QApplication([]) font = self.app.font() font.setPointSize(12) self.app.setFont(font) self.app.connect(self.app, QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), self._new_transaction_received) self.app.connect(self.app, QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), self._exchange_rate_updated) self.merchant_gui = MerchantGUI(self, self.currency) self.merchant_gui.show() self.customer_display = CustomerDisplay('data/customer_display.html', self.singleScreenMode) if not self.singleScreenMode: self.customer_display.show() self.app.exec_() def init_new_transaction(self, amount, currency): if self.singleScreenMode: self.customer_display.show() if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() if currency != "BTC": cur_amount = amount if self.exchange_rate != 0: amount = round(cur_amount / self.exchange_rate, 8) else: amount = 0 conversion = '["%.2f %s", "%.4f %s", "%s"]' % (cur_amount, currency, self.exchange_rate, self.exchange_rate_source, currency) else: conversion = '-1' self.current_address = self.bitcoind.getnewaddress("Point of Sale") self.merchant_gui.update_status("Looking for a transaction to %s..." % self.current_address) amount_str = self.format_btc_amount(amount) imgdata = self.create_img_data(self.current_address, amount_str) js = 'show_payment_info("%s", %s, "%s", "%s")' % \ ('%s BTC' % amount_str, conversion, self.current_address, imgdata) self.customer_display.evaluate_java_script(js) def create_img_data(self, address, amount_str): (_, size, img) = qrencode.encode("bitcoin:%s?amount=%s&label=" % (address, amount_str)) if size < 400: img = img.resize((400, 400), Image.NEAREST) buf = StringIO() img.save(buf, format='PNG') imgdata = "data:image/png,%s" % urllib.quote(buf.getvalue()) return imgdata def format_btc_amount(self, amount): s = "%.8f" % amount return re.sub("\.?0+$", "", s) # this is thread-safe, as long as it is called from a QThread def new_transaction_received(self, txid, output_addresses, from_green_address, green_address_msg): # emit signal, so we can process this on the Qt GUI thread self.app.emit(QtCore.SIGNAL('_new_transaction_received(PyQt_PyObject)'), (txid, output_addresses, from_green_address, green_address_msg)) def _new_transaction_received(self, data): (_, output_addresses, from_green_address, green_address_msg) = data if self.current_address != "" and self.current_address in output_addresses: msg = "Transaction to %s received." % self.current_address if from_green_address: msg += " " + green_address_msg self.merchant_gui.update_status(msg) self.customer_display.evaluate_java_script('show_payment_received()') self.current_address = "" def toggle_fullscreen_mode(self): if not self.customer_display.isFullScreen(): self.customer_display.showFullScreen() else: self.customer_display.showNormal() def clear_customer_display(self): self.customer_display.evaluate_java_script('show_idle()') # this is thread-safe, as long as it is called from a QThread def exchange_rate_updated(self, rate, source): self.app.emit(QtCore.SIGNAL('_exchange_rate_updated(PyQt_PyObject)'), (rate, source)) def _exchange_rate_updated(self, data): (self.exchange_rate, self.exchange_rate_source) = data self.merchant_gui.update_exchange_rate(self.exchange_rate)