def query_utxo_set(self, txout, includeconf=False): if not isinstance(txout, list): txout = [txout] txids = [h[:64] for h in txout] txids = list(set(txids)) # remove duplicates # self.BLOCKR_MAX_ADDR_REQ_COUNT = 2 if len(txids) > self.BLOCKR_MAX_ADDR_REQ_COUNT: txids = chunks(txids, self.BLOCKR_MAX_ADDR_REQ_COUNT) else: txids = [txids] data = [] for ids in txids: blockr_url = 'https://' + self.blockr_domain + '.blockr.io/api/v1/tx/info/' blockr_data = btc.make_request_blockr(blockr_url + ','.join( ids))['data'] if not isinstance(blockr_data, list): blockr_data = [blockr_data] data += blockr_data result = [] for txo in txout: txdata = [d for d in data if d['tx'] == txo[:64]][0] vout = [v for v in txdata['vouts'] if v['n'] == int(txo[65:])][0] if "is_spent" in vout and vout['is_spent'] == 1: result.append(None) else: result_dict = {'value': int(Decimal(vout['amount']) * Decimal( '1e8')), 'address': vout['address'], 'script': vout['extras']['script']} if includeconf: result_dict['confirms'] = int(txdata['confirmations']) result.append(result_dict) return result
def sync_addresses(self, wallet): log.info('downloading wallet history') # sets Wallet internal indexes to be at the next unused address for mix_depth in range(wallet.max_mix_depth): for forchange in [0, 1]: unused_addr_count = 0 last_used_addr = '' while (unused_addr_count < wallet.gaplimit or not is_index_ahead_of_cache(wallet, mix_depth, forchange)): addrs = [wallet.get_new_addr(mix_depth, forchange) for _ in range(self.BLOCKR_MAX_ADDR_REQ_COUNT)] # TODO send a pull request to pybitcointools # because this surely should be possible with a function from it blockr_url = 'https://' + self.blockr_domain blockr_url += '.blockr.io/api/v1/address/txs/' data = btc.make_request_blockr(blockr_url + ','.join(addrs))['data'] for dat in data: if dat['nb_txs'] != 0: last_used_addr = dat['address'] unused_addr_count = 0 else: unused_addr_count += 1 if last_used_addr == '': wallet.index[mix_depth][forchange] = 0 else: next_avail_idx = max([wallet.addr_cache[last_used_addr][ 2] + 1, wallet.index_cache[mix_depth][forchange]]) wallet.index[mix_depth][forchange] = next_avail_idx
def sync_unspent(self, wallet): # finds utxos in the wallet st = time.time() # dont refresh unspent dict more often than 10 minutes rate_limit_time = 10 * 60 if st - self.last_sync_unspent < rate_limit_time: log.info( 'blockr sync_unspent() happened too recently (%dsec), skipping' % (st - self.last_sync_unspent)) return wallet.unspent = {} addrs = wallet.addr_cache.keys() if len(addrs) == 0: log.info('no tx used') return i = 0 while i < len(addrs): inc = min(len(addrs) - i, self.BLOCKR_MAX_ADDR_REQ_COUNT) req = addrs[i:i + inc] i += inc # TODO send a pull request to pybitcointools # unspent() doesnt tell you which address, you get a bunch of utxos # but dont know which privkey to sign with blockr_url = 'https://' + self.blockr_domain + \ '.blockr.io/api/v1/address/unspent/' data = btc.make_request_blockr(blockr_url + ','.join(req))['data'] if 'unspent' in data: data = [data] for dat in data: for u in dat['unspent']: wallet.unspent[u['tx'] + ':' + str(u['n'])] = { 'address': dat['address'], 'value': int(u['amount'].replace('.', '')) } for u in wallet.spent_utxos: wallet.unspent.pop(u, None) self.last_sync_unspent = time.time() log.debug('blockr sync_unspent took ' + str((self.last_sync_unspent - st )) + 'sec')
def test_blockr_error_429(make_request): error = { u'code': 429, u'data': None, u'message': u'Too many requests. Wait a bit...', u'status': u'error' } success = { u'code': 200, u'data': { u'address': u'mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb', u'limit_txs': 200, u'nb_txs': 1, u'nb_txs_displayed': 1, u'txs': [{ u'amount': 1, u'amount_multisig': 0, u'confirmations': 400, u'time_utc': u'2016-09-15T19:46:14Z', u'tx': u'6a1bfbdd011cbb2ab2a000d477bd6372150238b4c24e43a850220dba4dbf2c0d' }] }, u'message': u'', u'status': u'success' } make_request.side_effect = map(json.dumps, [error] * 3 + [success]) d = btc.make_request_blockr(blockr_root_url + "address/txs/", "mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb") assert d['code'] == 200 assert d['data'] is not None
def test_blockr_error_429(make_request): error = {u'code': 429, u'data': None, u'message': u'Too many requests. Wait a bit...', u'status': u'error'} success = {u'code': 200, u'data': {u'address': u'mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb', u'limit_txs': 200, u'nb_txs': 1, u'nb_txs_displayed': 1, u'txs': [{u'amount': 1, u'amount_multisig': 0, u'confirmations': 400, u'time_utc': u'2016-09-15T19:46:14Z', u'tx': u'6a1bfbdd011cbb2ab2a000d477bd6372150238b4c24e43a850220dba4dbf2c0d'}]}, u'message': u'', u'status': u'success'} make_request.side_effect = map(json.dumps, [error]*3 + [success]) d = btc.make_request_blockr(blockr_root_url + "address/txs/", "mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb") assert d['code'] == 200 assert d['data'] is not None
def test_blockr_bad_request(): with pytest.raises(Exception) as e_info: btc.make_request_blockr(blockr_root_url+"address/txs/", "0000")
def test_blockr_bad_request(): with pytest.raises(Exception) as e_info: btc.make_request_blockr(blockr_root_url + "address/txs/", "0000")
def run(self): st = int(time.time()) unconfirmed_txid = None unconfirmed_txhex = None while not unconfirmed_txid: time.sleep(unconfirm_poll_period) if int(time.time()) - st > unconfirm_timeout: log.warn('checking for unconfirmed tx timed out') if self.timeoutfun: self.timeoutfun(False) return blockr_url = 'https://' + self.blockr_domain blockr_url += '.blockr.io/api/v1/address/unspent/' random.shuffle(self.output_addresses ) # seriously weird bug with blockr.io data = btc.make_request_blockr(blockr_url + ','.join( self.output_addresses) + '?unconfirmed=1')['data'] shared_txid = None for unspent_list in data: txs = set([str(txdata['tx']) for txdata in unspent_list['unspent']]) if not shared_txid: shared_txid = txs else: shared_txid = shared_txid.intersection(txs) log.info('sharedtxid = ' + str(shared_txid)) if len(shared_txid) == 0: continue time.sleep( 2 ) # here for some race condition bullshit with blockr.io blockr_url = 'https://' + self.blockr_domain blockr_url += '.blockr.io/api/v1/tx/raw/' data = btc.make_request_blockr(blockr_url + ','.join( shared_txid))['data'] if not isinstance(data, list): data = [data] for txinfo in data: txhex = str(txinfo['tx']['hex']) outs = set([(sv['script'], sv['value']) for sv in btc.deserialize(txhex)['outs']]) log.debug('unconfirm query outs = ' + str(outs)) if outs == self.tx_output_set: unconfirmed_txid = txinfo['tx']['txid'] unconfirmed_txhex = str(txinfo['tx']['hex']) break self.unconfirmfun( btc.deserialize(unconfirmed_txhex), unconfirmed_txid) st = int(time.time()) confirmed_txid = None confirmed_txhex = None while not confirmed_txid: time.sleep(confirm_poll_period) if int(time.time()) - st > confirm_timeout: log.warn('checking for confirmed tx timed out') if self.timeoutfun: self.timeoutfun(True) return blockr_url = 'https://' + self.blockr_domain blockr_url += '.blockr.io/api/v1/address/txs/' data = btc.make_request_blockr(blockr_url + ','.join( self.output_addresses))['data'] shared_txid = None for addrtxs in data: txs = set(str(txdata['tx']) for txdata in addrtxs['txs']) if not shared_txid: shared_txid = txs else: shared_txid = shared_txid.intersection(txs) log.info('sharedtxid = ' + str(shared_txid)) if len(shared_txid) == 0: continue blockr_url = 'https://' + self.blockr_domain blockr_url += '.blockr.io/api/v1/tx/raw/' data = btc.make_request_blockr(blockr_url + ','.join( shared_txid))['data'] if not isinstance(data, list): data = [data] for txinfo in data: txhex = str(txinfo['tx']['hex']) outs = set([(sv['script'], sv['value']) for sv in btc.deserialize(txhex)['outs']]) log.debug('confirm query outs = ' + str(outs)) if outs == self.tx_output_set: confirmed_txid = txinfo['tx']['txid'] confirmed_txhex = str(txinfo['tx']['hex']) break self.confirmfun( btc.deserialize(confirmed_txhex), confirmed_txid, 1)