pybitcash / bitcash

BitCash: Python Bitcoin Cash Library (fork of ofek's Bit)
https://bitcash.dev
MIT License
97 stars 39 forks source link

ConnectionError: All APIs are unreachable #115

Closed kratatomi closed 1 year ago

kratatomi commented 2 years ago

A program I develop based on BitCash worked without issues since the last days. I started to get a ConnectionError every time I tried to broadcast a tx: "Transaction broadcast failed or unspends were already used". Now, all APIs are unreachable. I don't use any custom API, does BitCash use rest1.biggestfan.net as default? Is it giving troubles to anyone else?

EDIT: Despite the "All APIs are unreachable" error message, the script is working and txs are being broadcasted.

merc1er commented 2 years ago

A program I develop based on BitCash worked without issues since the last days.

I started to get a ConnectionError every time I tried to broadcast a tx: "Transaction broadcast failed or unspends were already used". Now, all APIs are unreachable. I don't use any custom API, does BitCash use rest1.biggestfan.net as default? Is it giving troubles to anyone else?

EDIT: Despite the "All APIs are unreachable" error message, the script is working and txs are being broadcasted.

Yes I'm having the same issue. Will reach out to BiggestFan.

kratatomi commented 2 years ago

Just to leave some more info, reading the docs it seems that BitCash uses BitPay API. Browsing https://bitpay.com/insight/#/ALL/mainnet/home I found that a 429 error is raised for BCH and many other networks: Error 429: Http failure response for https://api.bitcore.io/api/BCH/mainnet/block?limit=5: 429 Too Many Requests

Nvm, I've been reading the Python code and it seems that BitCash uses https://rest.bch.actorforth.org/v2/ as first option by default. I tried sending a raw transaction using their web interface and it worked. The raw tx was constructed from BitCash, so it's the broadcasting the failing point. I always broadcast the tx this way: NetworkAPI.broadcast_tx(my_tx)

merc1er commented 2 years ago

Just to leave some more info, reading the docs it seems that BitCash uses BitPay API. Browsing https://bitpay.com/insight/#/ALL/mainnet/home I found that a 429 error is raised for BCH and many other networks:

Error 429: Http failure response for https://api.bitcore.io/api/BCH/mainnet/block?limit=5: 429 Too Many Requests

Nvm, I've been reading the Python code and it seems that BitCash uses https://rest.bch.actorforth.org/v2/ as first option by default. I tried sending a raw transaction using their web interface and it worked. The raw tx was constructed from BitCash, so it's the broadcasting the failing point. I always broadcast the tx this way:

NetworkAPI.broadcast_tx(my_tx)

BitPay is used to calculate the fiat rate.

Yes I tried sending transactions through the web interface directly and it worked. It also works if I query the UTXO, wait 1 second then broadcast the transaction.

I will try to write a script that reproduces the issue so I can investigate this with BiggestFan

willcharlton commented 2 years ago

I'm not sure if this is related or not, but I am getting the same error when trying to get_transactions().

Code

#!.venv/bin/python

# .venv/bin/pip install coincurve bitcash qrcode[pil]

import os
import qrcode
import bitcash
from bitcash import Key, PrivateKeyTestnet

class Wallet(object):
    BCH_DIR = os.path.join(os.path.dirname(__file__), '.bch')
    def __init__(self, name, net='mainnet'):
        self.name = name
        self.net = net
        bch_dir = f'{self.BCH_DIR}' if net == 'mainnet' else f'{self.BCH_DIR}{self.net}'
        if not os.path.isdir(bch_dir):
            os.mkdir(bch_dir)
        key_dir = os.path.join(bch_dir, name)
        if not os.path.isdir(key_dir):
            os.mkdir(key_dir)
        self._key_path = os.path.join(key_dir, 'pkey')
        if not os.path.exists(self._key_path):
            with open(self._key_path, 'w+') as pkey:
                print(f'creating wallet {name} private key on net: {net}')
                key = Key() if net != 'test' else PrivateKeyTestnet(network=net)
                pkey.write(str(key.to_pem().decode()))
        with open(self._key_path, 'r') as pkey:
            K = Key if net != 'test' else PrivateKeyTestnet
            self._key = K.from_pem(pkey.read().encode('utf-8')) 
        self._qr_path = os.path.join(key_dir, 'address_qr.png')
        if not os.path.exists(self._qr_path):
            with open(self._qr_path, 'w+') as pkey:
                print(f'creating qr image for {name} private key on net: {net}')
                qr = qrcode.QRCode(
                    version=1,
                    error_correction=qrcode.constants.ERROR_CORRECT_L,
                    box_size=10,
                    border=4,
                )
                qr.add_data(key.address)
                qr.make(fit=True)
                img = qr.make_image(fill_color="black", back_color="white")
                img.save(self._qr_path, format=img.format)

    @property
    def key_path(self):
        return self._key_path
    @property
    def key(self):
        return self._key
    @property
    def address(self):
        return self._key.address
    @property
    def balance(self, currency='bch'):
        return self._key.balance_as(currency)
    @property
    def transactions(self):
        return self._key.get_transactions()
    @property
    def qr_path(self):
        return self._qr_path

if __name__ == '__main__':
    wa = Wallet('a', net='test')
    wb = Wallet('b', net='test')
    for w in [wa, wb]:
        print(f'wallet {w.name}: {w.address} -> {w.balance}')
    for w in [wa, wb]:
        print(f'keytype {w.name}: {type(w.key)}')
    for w in [wa, wb]:
        print(f'transactions {w.name}: {w.transactions}')

Output

$ ./bch-ping-pong.py 
wallet a: bchtest:qqgsu0pcsyp3c23yxn5ny56fpa6yzv33nswsnn0zz9 -> 0
wallet b: bchtest:qp2zw5e32k2cc5jzkgp6e3y625nh89qm3urwc6vxaj -> 0
keytype a: <class 'bitcash.wallet.PrivateKeyTestnet'>
keytype b: <class 'bitcash.wallet.PrivateKeyTestnet'>
Traceback (most recent call last):
  File "./bch-ping-pong.py", line 71, in <module>
    print(f'transactions {w.name}: {w.transactions}')
  File "./bch-ping-pong.py", line 57, in transactions
    return self._key.get_transactions()
  File "/Users/will/williamcharltonengineering/bch-ping-pong/.venv/lib/python3.8/site-packages/bitcash/wallet.py", line 238, in get_transactions
    self.transactions[:] = NetworkAPI.get_transactions(
  File "/Users/will/williamcharltonengineering/bch-ping-pong/.venv/lib/python3.8/site-packages/bitcash/network/services.py", line 112, in get_transactions
    raise ConnectionError("All APIs are unreachable.")  # pragma: no cover
ConnectionError: All APIs are unreachable.

If I switch to net='mainnet' in the Wallet constructor then it works.

merc1er commented 2 years ago

@willcharlton could you submit a minimal reproducible example?

With that I would be able to investigate this issue further.

merc1er commented 1 year ago

This should be fixed.