<gh_stars>1-10
import json
import socket
from hashlib import sha256
import time
from bitcoin.wallet import CBitcoinAddress
import bitcoin
import binascii
from typing import Dict, List
from config import Config, NETWORK_TESTNET, NETWORK_MAINNET
import logging

def _request(method, *args):
    return {'method': method, 'params': list(args)}

def _to_scripthash(bitcoin_address: CBitcoinAddress) -> str:
    '''
    @return: hex string.
    '''
    script_pubkey = bitcoin_address.to_scriptPubKey()
    h = sha256(script_pubkey).digest()
    h = h[::-1]
    return binascii.hexlify(h).decode("ascii")

class ElectrsClient:
    def __init__(self, addr):
        self.s = socket.create_connection(addr)
        self.f = self.s.makefile('r')
        self.id = 0

        version_response = self._call(_request("server.version", "", "1.4"))
        assert version_response.get(
            "error") is None, "Failed to nogatiate veresion"

    def close(self):
        self.f.close()
        self.s.close()

    def _call(self, request):
        request['id'] = self.id
        request['jsonrpc'] = '2.0'
        self.id += 1

        msg = json.dumps(request) + '\n'
        self.s.sendall(msg.encode('ascii'))
        response = json.loads(self.f.readline())
        return response

    def _batch_call(self, requests):
        for request in requests:
            request['id'] = self.id
            request['jsonrpc'] = '2.0'
            self.id += 1

        msg = json.dumps(requests) + '\n'
        self.s.sendall(msg.encode('ascii'))
        responses = json.loads(self.f.readline())
        return responses

    def get_transactions(self, txids: List[str]):
        """
        @return: List of transactions in JSON in the same order as the input. 
        """
        requests = [_request("blockchain.transaction.get",
                             txid, True) for txid in txids]
        responses = self._batch_call(requests)
        txs = []
        for i, response in enumerate(responses):
            assert response.get("error") is None, "error: {}".format(
                response.get['error'])
            # Requests order must matches the reponses order
            assert response["result"]["txid"] == txids[i]
            txs.append(response["result"])
        return txs

    def existaddress(self, bitcoin_address: CBitcoinAddress) -> bool:
        response = self._call(_request("blockchain.scripthash.get_history",
                                       _to_scripthash(bitcoin_address)))
        assert response.get('error') is None, "error: {}".format(
            response.get['error'])
        return len(response['result']) > 0

    def get_history(self, bitcoin_address: CBitcoinAddress) -> List[str]:
        """
        @return: A list of txid of confirmed transaction in blockchain order (e.g 2094994, 2094998, ... ). 
            txid is in hex string e.g "68278fbf26dfb5e280ba298cf621775c6a20fbf5606dd6ce9443d314950cb2f7" for testnet.
        """
        response = self._call(_request("blockchain.scripthash.get_history",
                                       _to_scripthash(bitcoin_address)))
        txids = []
        for r in response['result']:
            if r["height"] > 0:
                txids.append(r["tx_hash"])
        return txids

    def get_mempool(self, bitcoin_address: CBitcoinAddress) -> List[str]:
        """
        @return: A list of txid of transaction in mempool.
        """
        response = self._call(_request("blockchain.scripthash.get_history",
                                   _to_scripthash(bitcoin_address)))
        txids = []
        for r in response['result']:
            if r["height"] == 0:
                txids.append(r["tx_hash"])
        return txids

    def fee_estimates(self):
        """
        Get an object where the key is the confirmation target (in number of blocks) and the value is the estimated feerate (in sat/vB).
        The available confirmation targets are 1-25, 144, 504 and 1008 blocks. 
        @return: exmaple { "1": 87.882, "6": 20.0, "144": 1.027 }
        """
        n_blocks_list = [1, 6, 144]
        requests = [_request("blockchain.estimatefee", n_blocks)
                    for n_blocks in n_blocks_list]
        responses = self._batch_call(requests)
        feerates = {}
        for i in range(len(responses)):
            feerates[str(n_blocks_list[i])] = responses[i]["result"]*100000
        return feerates
        
    def get_rawtransaction(self, txid: str) -> str:
        """
        @txid: The transaction hash as a hexadecimal string.
        @return: tx in hexadecimal string.
        """
        response = self._call(_request("blockchain.transaction.get", txid, False))
        assert response.get('error') is None, "error: {}".format(
            response.get['error'])
        return response["result"]

def creact_electrs_client() -> ElectrsClient:
    '''
    Caller owns the returned object, and it should be closed when no longer in used.
    '''
    assert Config.TestNetElectrsHostAndPort
    if Config.Network == NETWORK_TESTNET:
        return ElectrsClient(Config.TestNetElectrsHostAndPort)
    else:
        return ElectrsClient(Config.ElectrsHostAndPort)



import json
import socket
from hashlib import sha256
import time
from bitcoin.wallet import CBitcoinAddress
import bitcoin
import binascii
from typing import Dict, List
from config import Config, NETWORK_TESTNET, NETWORK_MAINNET
import logging

def _request(method, *args):
    return {'method': method, 'params': list(args)}

def _to_scripthash(bitcoin_address: CBitcoinAddress) -> str:
    '''
    @return: hex string.
    '''
    script_pubkey = bitcoin_address.to_scriptPubKey()
    h = sha256(script_pubkey).digest()
    h = h[::-1]
    return binascii.hexlify(h).decode("ascii")

class ElectrsClient:
    def __init__(self, addr):
        self.s = socket.create_connection(addr)
        self.f = self.s.makefile('r')
        self.id = 0

        version_response = self._call(_request("server.version", "", "1.4"))
        assert version_response.get(
            "error") is None, "Failed to nogatiate veresion"

    def close(self):
        self.f.close()
        self.s.close()

    def _call(self, request):
        request['id'] = self.id
        request['jsonrpc'] = '2.0'
        self.id += 1

        msg = json.dumps(request) + '\n'
        self.s.sendall(msg.encode('ascii'))
        response = json.loads(self.f.readline())
        return response

    def _batch_call(self, requests):
        for request in requests:
            request['id'] = self.id
            request['jsonrpc'] = '2.0'
            self.id += 1

        msg = json.dumps(requests) + '\n'
        self.s.sendall(msg.encode('ascii'))
        responses = json.loads(self.f.readline())
        return responses

    def get_transactions(self, txids: List[str]):
        """
        @return: List of transactions in JSON in the same order as the input. 
        """
        requests = [_request("blockchain.transaction.get",
                             txid, True) for txid in txids]
        responses = self._batch_call(requests)
        txs = []
        for i, response in enumerate(responses):
            assert response.get("error") is None, "error: {}".format(
                response.get['error'])
            # Requests order must matches the reponses order
            assert response["result"]["txid"] == txids[i]
            txs.append(response["result"])
        return txs

    def existaddress(self, bitcoin_address: CBitcoinAddress) -> bool:
        response = self._call(_request("blockchain.scripthash.get_history",
                                       _to_scripthash(bitcoin_address)))
        assert response.get('error') is None, "error: {}".format(
            response.get['error'])
        return len(response['result']) > 0

    def get_history(self, bitcoin_address: CBitcoinAddress) -> List[str]:
        """
        @return: A list of txid of confirmed transaction in blockchain order (e.g 2094994, 2094998, ... ). 
            txid is in hex string e.g "68278fbf26dfb5e280ba298cf621775c6a20fbf5606dd6ce9443d314950cb2f7" for testnet.
        """
        response = self._call(_request("blockchain.scripthash.get_history",
                                       _to_scripthash(bitcoin_address)))
        txids = []
        for r in response['result']:
            if r["height"] > 0:
                txids.append(r["tx_hash"])
        return txids

    def get_mempool(self, bitcoin_address: CBitcoinAddress) -> List[str]:
        """
        @return: A list of txid of transaction in mempool.
        """
        response = self._call(_request("blockchain.scripthash.get_history",
                                   _to_scripthash(bitcoin_address)))
        txids = []
        for r in response['result']:
            if r["height"] == 0:
                txids.append(r["tx_hash"])
        return txids

    def fee_estimates(self):
        """
        Get an object where the key is the confirmation target (in number of blocks) and the value is the estimated feerate (in sat/vB).
        The available confirmation targets are 1-25, 144, 504 and 1008 blocks. 
        @return: exmaple { "1": 87.882, "6": 20.0, "144": 1.027 }
        """
        n_blocks_list = [1, 6, 144]
        requests = [_request("blockchain.estimatefee", n_blocks)
                    for n_blocks in n_blocks_list]
        responses = self._batch_call(requests)
        feerates = {}
        for i in range(len(responses)):
            feerates[str(n_blocks_list[i])] = responses[i]["result"]*100000
        return feerates
        
    def get_rawtransaction(self, txid: str) -> str:
        """
        @txid: The transaction hash as a hexadecimal string.
        @return: tx in hexadecimal string.
        """
        response = self._call(_request("blockchain.transaction.get", txid, False))
        assert response.get('error') is None, "error: {}".format(
            response.get['error'])
        return response["result"]

def creact_electrs_client() -> ElectrsClient:
    '''
    Caller owns the returned object, and it should be closed when no longer in used.
    '''
    assert Config.TestNetElectrsHostAndPort
    if Config.Network == NETWORK_TESTNET:
        return ElectrsClient(Config.TestNetElectrsHostAndPort)
    else:
        return ElectrsClient(Config.ElectrsHostAndPort)
@return: hex string. @return: List of transactions in JSON in the same order as the input. # Requests order must matches the reponses order @return: A list of txid of confirmed transaction in blockchain order (e.g 2094994, 2094998, ... ).              txid is in hex string e.g "68278fbf26dfb5e280ba298cf621775c6a20fbf5606dd6ce9443d314950cb2f7" for testnet. @return: A list of txid of transaction in mempool. Get an object where the key is the confirmation target (in number of blocks) and the value is the estimated feerate (in sat/vB).         The available confirmation targets are 1-25, 144, 504 and 1008 blocks.          @return: exmaple { "1": 87.882, "6": 20.0, "144": 1.027 } @txid: The transaction hash as a hexadecimal string.         @return: tx in hexadecimal string. Caller owns the returned object, and it should be closed when no longer in used.