Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Commit

Permalink
remove all code and tests for blockr.io
Browse files Browse the repository at this point in the history
the old warning when running yield generators off blockr.io now
warns about using blockchain.info, in case anybody uses that.
  • Loading branch information
adlai committed Oct 3, 2017
1 parent 02c5c96 commit 46fe3e8
Show file tree
Hide file tree
Showing 7 changed files with 14 additions and 463 deletions.
31 changes: 0 additions & 31 deletions bitcoin/bci.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,39 +35,8 @@ def make_request(*args):
p = e
raise Exception(p)

def make_request_blockr(*args):
counter = 0
while True:
data = json.loads(make_request(*args))
if data['status'] == 'error' and data['code'] == 429:
log.error('Blockr service error: ' + data['message'])
time.sleep(min(60, 2**counter / 2.))
counter += 1
continue
return data

# Pushes a transaction to the network using https://blockchain.info/pushtx
def bci_pushtx(tx):
if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
return make_request('https://blockchain.info/pushtx', 'tx=' + tx)

def blockr_pushtx(tx, network='btc'):
if network == 'testnet':
blockr_url = 'https://tbtc.blockr.io/api/v1/tx/push'
elif network == 'btc':
blockr_url = 'https://btc.blockr.io/api/v1/tx/push'
else:
raise Exception('Unsupported network {0} for blockr_pushtx'.format(
network))

if not re.match('^[0-9a-fA-F]*$', tx):
tx = tx.encode('hex')
return make_request(blockr_url, '{"hex":"%s"}' % tx)







3 changes: 2 additions & 1 deletion joinmarket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
get_network, jm_single, get_network, validate_address, get_irc_mchannels, \
check_utxo_blacklist
from .electruminterface import ElectrumInterface
from .blockchaininterface import BlockrInterface, BlockchainInterface, sync_wallet
from .blockchaininterface import BlockchainInterface, sync_wallet, \
BlockchaininfoInterface
from .yieldgenerator import YieldGenerator, ygmain
from .peertopeer import P2PProtocol, P2PMessageHandler, tor_broadcast_tx
# Set default logging handler to avoid "No handler found" warnings.
Expand Down
281 changes: 2 additions & 279 deletions joinmarket/blockchaininterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ def __init__(self, testnet=False):

# blockchain.info doesn't support testnet via API :(
if testnet:
log.info("Blockchain.info doesn't support testnet, "
"try blockr as blockchain source. Quitting.")
log.info("Blockchain.info doesn't support testnet."
"Try using a local core node for testnet. Quitting.")
return None

# see bci.py in bitcoin module
Expand Down Expand Up @@ -486,283 +486,6 @@ def estimate_fee_per_kb(self, N):

return fee_per_kb

class BlockrInterface(BlockchainInterface):
BLOCKR_MAX_ADDR_REQ_COUNT = 20

def __init__(self, testnet=False):
super(BlockrInterface, self).__init__()

# see bci.py in bitcoin module
self.network = 'testnet' if testnet else 'btc'
self.blockr_domain = 'tbtc' if testnet else 'btc'
self.last_sync_unspent = 0

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 add_tx_notify(self,
txd,
unconfirmfun,
confirmfun,
notifyaddr,
timeoutfun=None):
unconfirm_timeout = jm_single().config.getint('TIMEOUT',
'unconfirm_timeout_sec')
unconfirm_poll_period = 5
confirm_timeout = jm_single().config.getfloat(
'TIMEOUT', 'confirm_timeout_hours') * 60 * 60
confirm_poll_period = 5 * 60

class NotifyThread(threading.Thread):

def __init__(self, blockr_domain, txd, unconfirmfun, confirmfun,
timeoutfun):
threading.Thread.__init__(self, name='BlockrNotifyThread')
self.daemon = True
self.blockr_domain = blockr_domain
self.unconfirmfun = unconfirmfun
self.confirmfun = confirmfun
self.timeoutfun = timeoutfun
self.tx_output_set = set([(sv['script'], sv['value'])
for sv in txd['outs']])
self.output_addresses = [
btc.script_to_address(scrval[0], get_p2pk_vbyte())
for scrval in self.tx_output_set
]
log.debug('txoutset=' + pprint.pformat(self.tx_output_set))
log.debug('outaddrs=' + ','.join(self.output_addresses))

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)

NotifyThread(self.blockr_domain, txd, unconfirmfun, confirmfun,
timeoutfun).start()

def pushtx(self, txhex):
try:
json_str = btc.blockr_pushtx(txhex, self.network)
data = json.loads(json_str)
if data['status'] != 'success':
log.debug(data)
return False
except Exception:
log.error('failed blockr.io pushtx')
log.debug(traceback.format_exc())
return False
return True

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 estimate_fee_per_kb(self, N):
bcypher_fee_estimate_url = 'https://api.blockcypher.com/v1/btc/main'
bcypher_data = json.loads(btc.make_request(bcypher_fee_estimate_url))
log.debug("Got blockcypher result: " + pprint.pformat(bcypher_data))
if N <= 2:
fee_per_kb = bcypher_data["high_fee_per_kb"]
elif N <= 4:
fee_per_kb = bcypher_data["medium_fee_per_kb"]
else:
fee_per_kb = bcypher_data["low_fee_per_kb"]

return fee_per_kb


def bitcoincore_timeout_callback(uc_called, txout_set, txnotify_fun_list,
timeoutfun):
log.debug('bitcoin core timeout callback uc_called = %s' % ('true'
Expand Down
6 changes: 2 additions & 4 deletions joinmarket/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def jm_single():
# Which blockchain backend to use
# only change this setting if you are UNABLE to use a bitcoin node!
blockchain_source = bitcoin-rpc
# alternatives: electrum, bc.i, blockr, regtest
# alternatives: electrum, bc.i, regtest
# If you pick the latter, you'll need to change this accordingly
network = mainnet
# The rest of this section pertains only to the bitcoin-rpc backend
Expand Down Expand Up @@ -369,7 +369,7 @@ def get_blockchain_interface_instance(_config):
# todo: refactor joinmarket module to get rid of loops
# importing here is necessary to avoid import loops
from joinmarket.blockchaininterface import BitcoinCoreInterface, \
RegtestBitcoinCoreInterface, BlockrInterface, BlockchaininfoInterface
RegtestBitcoinCoreInterface, BlockchaininfoInterface
from joinmarket.electruminterface import ElectrumInterface
from joinmarket.blockchaininterface import CliJsonRpc

Expand All @@ -395,8 +395,6 @@ def get_blockchain_interface_instance(_config):
rpc_password = _config.get("BLOCKCHAIN", "rpc_password")
rpc = JsonRpc(rpc_host, rpc_port, rpc_user, rpc_password)
bc_interface = RegtestBitcoinCoreInterface(rpc)
elif source == 'blockr':
bc_interface = BlockrInterface(testnet)
elif source == 'electrum':
try:
electrum_server = _config.get("BLOCKCHAIN", "electrum_server")
Expand Down
Loading

0 comments on commit 46fe3e8

Please sign in to comment.