예제 #1
0
파일: base.py 프로젝트: ericecook/powerpool
 def _start_monitor_nodes(self):
     for serv in self.config['coinservs']:
         conn = CoinserverRPC(
             "http://{0}:{1}@{2}:{3}/"
             .format(serv['username'],
                     serv['password'],
                     serv['address'],
                     serv['port']),
             pool_kwargs=dict(maxsize=serv.get('maxsize', 10)))
         conn.config = serv
         conn.name = "{}:{}".format(serv['address'], serv['port'])
         self._down_connections.append(conn)
예제 #2
0
    def __init__(self, bootstrap):
        ConfigObject.__init__(self, bootstrap)
        self.coinserv = CoinserverRPC("http://{0}:{1}@{2}:{3}/".format(
            bootstrap['coinserv']['username'],
            bootstrap['coinserv']['password'],
            bootstrap['coinserv']['address'],
            bootstrap['coinserv']['port'],
            pool_kwargs=dict(maxsize=bootstrap.get('maxsize', 10))))
        self.exchangeable = bool(self.exchangeable)
        self.minimum_payout = dec(self.minimum_payout)

        # If a pool payout addr is specified, make sure it matches the
        # configured address version.
        if self.pool_payout_addr is not None:
            try:
                ver = address_version(self.pool_payout_addr)
            except (KeyError, AttributeError):
                ver = None
            if ver not in self.address_version:
                raise ConfigurationException(
                    "{} is not a valid {} address. Must be version {}, got version {}"
                    .format(self.pool_payout_addr, self.key,
                            self.address_version, ver))

        # Check to make sure there is a configured pool address for
        # unexchangeable currencies
        if self.exchangeable is False and self.pool_payout_addr is None:
            raise ConfigurationException(
                "Unexchangeable currencies require a pool payout addr."
                "No valid address found for {}".format(self.key))
예제 #3
0
    def __init__(self, config, logger=None):
        if not config:
            raise CoinRPCException(
                {'code': -1, 'message': 'Invalid configuration file'})

        self._set_config(**config)

        if logger:
            self.logger = logger
        else:
            logging.Formatter.converter = datetime.time.gmtime
            self.logger = logging.getLogger(self.config['logger_name'])
            self.logger.setLevel(getattr(logging, self.config['log_level']))

        self.conn = CoinserverRPC("http://{0}:{1}@{2}:{3}/"
            .format(self.coinserv['username'], self.coinserv['password'],
                    self.coinserv['address'], self.coinserv['port'],
                    pool_kwargs=dict(maxsize=self.maxsize)))
예제 #4
0
    def __init__(self, bootstrap):
        bootstrap['_algo'] = bootstrap.pop('algo', None)
        if bootstrap['_algo'] is None:
            raise ConfigurationException(
                "A currency in config.toml is missing an entry in "
                "defaults.toml! The following config may help identify it: {}".
                format(bootstrap))

        ConfigObject.__init__(self, bootstrap)
        if self.coinserv:
            cfg = self.coinserv
            self.coinserv = CoinserverRPC("http://{0}:{1}@{2}:{3}/".format(
                cfg['username'],
                cfg['password'],
                cfg['address'],
                cfg['port'],
                pool_kwargs=dict(maxsize=bootstrap.get('maxsize', 10))))
            self.coinserv.config = cfg
        elif self.sellable or self.mineable or self.buyable:
            raise ConfigurationException(
                "Coinserver must be configured for {}!".format(self.key))

        self.sellable = bool(self.sellable)
        self.buyable = bool(self.buyable)
        self.merged = bool(self.merged)
        self.minimum_payout = dec(self.minimum_payout)

        # If a pool payout addr is specified, make sure it matches the
        # configured address version.
        if self.pool_payout_addr is not None:
            try:
                ver = address_version(self.pool_payout_addr)
            except (KeyError, AttributeError):
                ver = None
            if ver not in self.address_version:
                raise ConfigurationException(
                    "{} is not a valid {} address. Must be version {}, got version {}"
                    .format(self.pool_payout_addr, self.key,
                            self.address_version, ver))

        # Check to make sure there is a configured pool address for
        # unsellable currencies
        if self.sellable is False and self.pool_payout_addr is None and self.mineable:
            raise ConfigurationException(
                "Unsellable currencies require a pool payout addr."
                "No valid address found for {}".format(self.key))
예제 #5
0
    def __init__(self, config, logger=None):
        if not config:
            raise CoinRPCException(
                {'code': -1, 'message': 'Invalid configuration file'})

        self._set_config(**config)

        if logger:
            self.logger = logger
        else:
            logging.Formatter.converter = datetime.time.gmtime
            self.logger = logging.getLogger(self.config['logger_name'])
            self.logger.setLevel(getattr(logging, self.config['log_level']))

        self.conn = CoinserverRPC("http://{0}:{1}@{2}:{3}/"
            .format(self.coinserv['username'], self.coinserv['password'],
                    self.coinserv['address'], self.coinserv['port'],
                    pool_kwargs=dict(maxsize=self.maxsize)))
예제 #6
0
ch.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
root.addHandler(ch)
root.setLevel(getattr(logging, args.log_level))

config = {'pass': None}
for line in args.config_path:
    pts = line.strip().split("=")
    if len(pts) != 2:
        continue
    config[pts[0]] = pts[1]

if args.passphrase_file:
    config['pass'] = args.passphrase_file.read().strip()

rpc_connection = CoinserverRPC(
    "http://{rpcuser}:{rpcpassword}@localhost:{rpcport}/"
    .format(**config))

try:
    balance = rpc_connection.getbalance()
except CoinRPCException as e:
    logging.error("Unable to retrieve balance, rpc returned {}".format(e))
    exit(1)

try:
    balance = Decimal(balance)
except ValueError:
    logging.error("Bogus data returned by balance call, exiting"
                  .format(str(balance)[:100]))
    exit(1)
예제 #7
0
 def getinfo(self, *args, **kwargs):
     res = CoinserverRPC.__getattr__(self, "getinfo")
     res = res(*args, **kwargs)
     self.last_getinfo = res
     self.last_getinfo['time'] = time.time()
     return res
예제 #8
0
 def __init__(self, *args, **kwargs):
     CoinserverRPC.__init__(self, *args, **kwargs)
     self._conn.ConnectionCls = TimedHTTPConnection
     self.last_getinfo = None
     self.name = None
예제 #9
0
ch.setLevel(logging.DEBUG)
ch.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
root.addHandler(ch)
root.setLevel(getattr(logging, args.log_level))

config = {'pass': None}
for line in args.config_path:
    pts = line.strip().split("=")
    if len(pts) != 2:
        continue
    config[pts[0]] = pts[1]

if args.passphrase_file:
    config['pass'] = args.passphrase_file.read().strip()

rpc_connection = CoinserverRPC(
    "http://{rpcuser}:{rpcpassword}@localhost:{rpcport}/".format(**config))

try:
    balance = rpc_connection.getbalance()
except CoinRPCException as e:
    logging.error("Unable to retrieve balance, rpc returned {}".format(e))
    exit(1)

try:
    balance = Decimal(balance)
except ValueError:
    logging.error("Bogus data returned by balance call, exiting".format(
        str(balance)[:100]))
    exit(1)

if balance < Decimal("0.001"):
예제 #10
0
class CoinRPC(object):
    """
    Treating the RPC as a python object is pretty convenient. All possible
    exceptions thrown by RPC code are raised as CoinRPCException, making
    error handling pretty easy
    """
    def __getattr__(self, attr):
        return self.config[attr]

    def _set_config(self, **kwargs):
        # A fast way to set defaults for the kwargs then set them as attributes
        self.config = dict(maxsize=10,
                           tx_fee=0,
                           min_confirms=6,
                           enabled=True,
                           account="",
                           logger_name="coin_rpc",
                           log_level="INFO")
        self.config.update(kwargs)

        required_conf = ['coinserv', 'currency_code']
        error = False
        for req in required_conf:
            if req not in self.config:
                print("{} is a required configuration variable".format(req))
                error = True

        coinserv_required_conf = ['username', 'password', 'address', 'port']
        for req in coinserv_required_conf:
            if req not in self.coinserv:
                print(
                    "{} is a required coinserv configuration variable".format(
                        req))
                error = True

        if error:
            raise CoinRPCException({
                'code':
                -1,
                'message':
                'Errors occurred while configuring '
                'CoinRPC obj'
            })

    def __init__(self, config, logger=None):
        if not config:
            raise CoinRPCException({
                'code': -1,
                'message': 'Invalid configuration file'
            })

        self._set_config(**config)

        if logger:
            self.logger = logger
        else:
            logging.Formatter.converter = datetime.time.gmtime
            self.logger = logging.getLogger(self.config['logger_name'])
            self.logger.setLevel(getattr(logging, self.config['log_level']))

        self.conn = CoinserverRPC("http://{0}:{1}@{2}:{3}/".format(
            self.coinserv['username'],
            self.coinserv['password'],
            self.coinserv['address'],
            self.coinserv['port'],
            pool_kwargs=dict(maxsize=self.maxsize)))

    @rpc_conn
    def poke_rpc(self):
        return self.conn.getnetworkinfo()

    @rpc_conn
    def set_tx_fee(self, amount):
        amount = float(amount)

        try:
            self.conn.settxfee(amount)
        except CoinRPCException:
            self.logger.warn("{} coinserver reported failure attempting to "
                             "set tx fee to {}".format(self.currency_code,
                                                       amount),
                             exc_info=True)
            raise
        else:
            self.logger.info("{} coinserver tx fee set to {}".format(
                self.currency_code, amount))

    @rpc_conn
    def get_transaction(self, coin_tx):
        """
        Runs gettransaction rpc call

        gettransaction sample output:

        {u'amount': Decimal('0.37810015'),
         u'blockhash': u'321e3dc1ad953ce585bbc2863effd29976f7f432e82e0af5a0e9a84ea096f373',
         u'blockindex': 2,
         u'blocktime': 1406482864,
         u'confirmations': 22,
         u'details': [{u'account': u'personal_mining_address',
                       u'address': u'VvAQomockqLj5uto6UKq7EXGAAxpkcWAio',
                       u'amount': Decimal('0.37810015'),
                       u'category': u'receive'}],
         u'time': 1406482863,
         u'timereceived': 1406482863,
         u'txid': u'd405e871740cc058e2121144362f18012f14e6d3ef702afa343c4c10469642c0'}
        """

        try:
            tx_info = self.conn.gettransaction(coin_tx)
        except CoinRPCException as e:
            if e.code == -5:
                self.logger.warn('Transaction {} not found in the {} '
                                 'wallet'.format(coin_tx, self.currency_code))
                raise
            else:
                raise

        self.logger.info("Found local info on {} TX {}".format(
            self.currency_code, coin_tx))
        self.logger.debug(pprint.pformat(tx_info))
        try:
            tx = CoinTransaction.create(tx_info, self.currency_code)
        except (KeyError, AssertionError):
            self.logger.warn('Got unexpected Coin RPC gettransaction '
                             'response \n{}'.format(pprint.pformat(tx_info)),
                             exc_info=True)
            raise CoinRPCException
        else:
            return tx

    @rpc_conn
    def list_transactions(self, account="''", count=10):
        """
        Runs the 'listtransactions' rpc call on the given currency's rpc server
        and returns transactions

        listtransactions sample output:

        [
            {
                "account" : "scm",
                "address" : "LKyGLZ4tLYjWEDGFzT94KvCdUFYvm7KyXj",
                "category" : "send",
                "amount" : -7.97790612,
                "fee" : 0.00000000,
                "confirmations" : 2819,
                "blockhash" : "2da8acefd9e9b5c0c2b0ccb655e1423c950a8c7877847c614f48a5e55235026f",
                "blockindex" : 1,
                "blocktime" : 1410849255,
                "txid" : "0e9b03a9d212263b1c28a9945463a8d3d451926885b2f2c2106f49c8fd6bbc95",
                "time" : 1410849251,
                "timereceived" : 1410849251
            }
        ]
        """
        result = self.conn.listtransactions(account, count)

        self.logger.debug("Received {} {} transactions".format(
            len(result), self.currency_code))

        transactions = []
        try:
            for tx_info in result:
                tx = CoinTransaction.create(tx_info, self.currency_code)
                transactions.append(tx)
        except KeyError as e:
            self.logger.warn(
                "Key error grabbing {} transactions. Got: {}".format(
                    self.currency_code, e))
            raise CoinRPCException
        else:
            return transactions

    @rpc_conn
    def unlock_wallet(self, seconds=10):
        if self.coinserv['wallet_pass']:
            try:
                wallet = self.conn.walletpassphrase(
                    self.coinserv['wallet_pass'], seconds)
            except CoinRPCException as e:
                # Some wallets get grumpy about unlock attempts when they're
                # not encrypted
                if e.code == -15:
                    self.logger.warn("Unlocking {} wallet unnecessary, its "
                                     "not encrypted. You should probably "
                                     "encrypt it...".format(
                                         self.currency_code))
            else:
                self.logger.info("Unlocking {} wallet. Success: {}".format(
                    self.currency_code, wallet))

    @rpc_conn
    def send_many(self, account, recip):
        """
        Runs the 'sendmany' rpc call on the given currency's rpc server
        """
        # Coercy account to a STR
        account = str(account)

        # Coerce all amounts to float
        for k, amount in recip.iteritems():
            recip[k] = float(amount)

        self.unlock_wallet()
        self.set_tx_fee(self.tx_fee)

        try:
            coin_tx = self.conn.sendmany(account, recip)
        except CoinRPCException as e:
            self.logger.error(
                "Unable to send funds! CoinRPC returned an error "
                "{}".format(e),
                exc_info=True)
            raise CoinRPCException
        else:
            self.logger.info("Successfully ran sendmany for {} to {}".format(
                self.currency_code, recip))

            # Lookup the transaction we just created to grab fees
            tx = self.get_transaction(coin_tx)

            return coin_tx, tx

    @rpc_conn
    def get_balance(self, account=None):
        """
        Runs the 'getbalance' rpc call on the given currency's rpc server
        """
        if not account and not self.account:
            balance = self.conn.getbalance()
        else:
            balance = self.conn.getbalance(account or self.account)

        self.logger.info("Found {} {} balance in local wallet".format(
            balance, self.currency_code))
        return balance

    @rpc_conn
    def get_block(self, block_hash):
        """
        Runs the 'getblock' rpc call on the given currency's rpc server
        """
        block_info = self.conn.getblock()
        self.logger.debug("For block {} received info:".format(
            block_hash, block_info))

        try:
            assert 'height' in block_info
            assert 'confirmations' in block_info
            assert 'hash' in block_info
        except AssertionError:
            self.logger.warn(
                'Got unexpected {} RPC getblock response. Got: {}'.format(
                    self.currency_code, pprint.pformat(block_info)),
                exc_info=True)

        return block_info

    @rpc_conn
    def get_transactions_since(self, blockhash, confirms=1):
        """
        Runs the 'listsinceblock' rpc call on the given currency's rpc server
        and returns transactions

        listsinceblock sample output:

        { "lastblock" : "0000000004ba22e9f8cea2e843b34f7eeaa2c3b7004ddcf19bfd8af0215fc0cc",
          "transactions" : [ { "account" : "",
                "address" : "mzE6DJMHPghYpVg4GCurMbxSSXBfW1KCFH",
                "amount" : 1.0,
                "category" : "receive",
                "confirmations" : 0,
                "time" : 1399200157,
                "timereceived" : 1399200157,
                "txid" : "917248d57293a7fd3a88aa3a26026d2e4d6a1d4eef898519b20419f2339c265c",
                "walletconflicts" : [  ]
              } ]
        }
        """
        result = self.conn.listsinceblock(blockhash, confirms)

        self.logger.info("For {} block {} received : {}".format(
            self.currency_code, blockhash, result))

        transactions = []
        try:
            for tx_info in result['transactions']:
                tx = CoinTransaction.create(tx_info, self.currency_code)
                transactions.append(tx)
            lastblock = result['lastblock']
        except KeyError as e:
            self.logger.warn("Key error grabbing {} transactions since {}"
                             " Got: {}".format(blockhash, self.currency_code,
                                               e))
            raise CoinRPCException
        else:
            return transactions, lastblock

    @rpc_conn
    def get_received(self, address, confirms=1):
        """
        Runs the 'receivedbyaddress' rpc call on the given currency's rpc
        server and returns tx ids
        """
        results = self.conn.receivedbyaddress(address, confirms)
        self.logger.info("For {} block {} received : {}".format(
            self.currency_code, results))
        for result in results:
            try:
                transactions = result['txids']
            except KeyError as e:
                self.logger.warn(
                    "Key error with {} txids for {}. Got: {}".format(
                        self.currency_code, address, e))
                raise CoinRPCException
            else:
                return transactions

    @rpc_conn
    def get_block_count(self):
        """
        Runs the 'getblockcount' rpc call on the given currency's rpc server
        and returns the count
        """
        count = self.conn.getblockcount()
        self.logger.info("Got {} height for: {}".format(
            count, self.currency_code))
        return count

    @rpc_conn
    def get_block_hash(self, index):
        """
        Runs the 'getblockhash' rpc call on the given currency's rpc server
        and returns the height
        """
        hash = self.conn.getblockhash(index)
        self.logger.info("For {} height {} received : {}".format(
            self.currency_code, index, hash))
        return hash
예제 #11
0
class CoinRPC(object):
    """
    Treating the RPC as a python object is pretty convenient. All possible
    exceptions thrown by RPC code are raised as CoinRPCException, making
    error handling pretty easy
    """

    def __getattr__(self, attr):
        return self.config[attr]

    def _set_config(self, **kwargs):
        # A fast way to set defaults for the kwargs then set them as attributes
        self.config = dict(maxsize=10,
                           tx_fee=0,
                           min_confirms=6,
                           enabled=True,
                           account="",
                           logger_name="coin_rpc",
                           log_level="INFO")
        self.config.update(kwargs)

        required_conf = ['coinserv', 'currency_code']
        error = False
        for req in required_conf:
            if req not in self.config:
                print("{} is a required configuration variable".format(req))
                error = True

        coinserv_required_conf = ['username', 'password', 'address', 'port']
        for req in coinserv_required_conf:
            if req not in self.coinserv:
                print("{} is a required coinserv configuration variable".format(req))
                error = True

        if error:
            raise CoinRPCException(
                {'code': -1, 'message': 'Errors occurred while configuring '
                                        'CoinRPC obj'})

    def __init__(self, config, logger=None):
        if not config:
            raise CoinRPCException(
                {'code': -1, 'message': 'Invalid configuration file'})

        self._set_config(**config)

        if logger:
            self.logger = logger
        else:
            logging.Formatter.converter = datetime.time.gmtime
            self.logger = logging.getLogger(self.config['logger_name'])
            self.logger.setLevel(getattr(logging, self.config['log_level']))

        self.conn = CoinserverRPC("http://{0}:{1}@{2}:{3}/"
            .format(self.coinserv['username'], self.coinserv['password'],
                    self.coinserv['address'], self.coinserv['port'],
                    pool_kwargs=dict(maxsize=self.maxsize)))

    @rpc_conn
    def poke_rpc(self):
        return self.conn.getinfo()

    @rpc_conn
    def set_tx_fee(self, amount):
        amount = float(amount)

        try:
            self.conn.settxfee(amount)
        except CoinRPCException:
            self.logger.warn("{} coinserver reported failure attempting to "
                             "set tx fee to {}".
                             format(self.currency_code, amount), exc_info=True)
            raise
        else:
            self.logger.info("{} coinserver tx fee set to {}".
                             format(self.currency_code, amount))

    @rpc_conn
    def get_transaction(self, coin_tx):
        """
        Runs gettransaction rpc call

        gettransaction sample output:

        {u'amount': Decimal('0.37810015'),
         u'blockhash': u'321e3dc1ad953ce585bbc2863effd29976f7f432e82e0af5a0e9a84ea096f373',
         u'blockindex': 2,
         u'blocktime': 1406482864,
         u'confirmations': 22,
         u'details': [{u'account': u'personal_mining_address',
                       u'address': u'VvAQomockqLj5uto6UKq7EXGAAxpkcWAio',
                       u'amount': Decimal('0.37810015'),
                       u'category': u'receive'}],
         u'time': 1406482863,
         u'timereceived': 1406482863,
         u'txid': u'd405e871740cc058e2121144362f18012f14e6d3ef702afa343c4c10469642c0'}
        """

        try:
            tx_info = self.conn.gettransaction(coin_tx)
        except CoinRPCException as e:
            if e.code == -5:
                self.logger.warn('Transaction {} not found in the {} '
                                 'wallet'.format(coin_tx, self.currency_code))
                raise
            else:
                raise

        self.logger.info("Found local info on {} TX {}"
                         .format(self.currency_code, coin_tx))
        self.logger.debug(pprint.pformat(tx_info))
        try:
            tx = CoinTransaction.create(tx_info, self.currency_code)
        except (KeyError, AssertionError):
            self.logger.warn('Got unexpected Coin RPC gettransaction '
                             'response \n{}'.format(pprint.pformat(tx_info)),
                             exc_info=True)
            raise CoinRPCException
        else:
            return tx

    @rpc_conn
    def list_transactions(self, account="''", count=10):
        """
        Runs the 'listtransactions' rpc call on the given currency's rpc server
        and returns transactions

        listtransactions sample output:

        [
            {
                "account" : "scm",
                "address" : "LKyGLZ4tLYjWEDGFzT94KvCdUFYvm7KyXj",
                "category" : "send",
                "amount" : -7.97790612,
                "fee" : 0.00000000,
                "confirmations" : 2819,
                "blockhash" : "2da8acefd9e9b5c0c2b0ccb655e1423c950a8c7877847c614f48a5e55235026f",
                "blockindex" : 1,
                "blocktime" : 1410849255,
                "txid" : "0e9b03a9d212263b1c28a9945463a8d3d451926885b2f2c2106f49c8fd6bbc95",
                "time" : 1410849251,
                "timereceived" : 1410849251
            }
        ]
        """
        result = self.conn.listtransactions(account, count)

        self.logger.debug("Received {} {} transactions"
                          .format(len(result), self.currency_code))

        transactions = []
        try:
            for tx_info in result:
                tx = CoinTransaction.create(tx_info, self.currency_code)
                transactions.append(tx)
        except KeyError as e:
            self.logger.warn("Key error grabbing {} transactions. Got: {}"
                             .format(self.currency_code, e))
            raise CoinRPCException
        else:
            return transactions

    @rpc_conn
    def unlock_wallet(self, seconds=10):
        if self.coinserv['wallet_pass']:
            try:
                wallet = self.conn.walletpassphrase(self.coinserv['wallet_pass'], seconds)
            except CoinRPCException as e:
                # Some wallets get grumpy about unlock attempts when they're
                # not encrypted
                if e.code == -15:
                    self.logger.warn("Unlocking {} wallet unnecessary, its "
                                     "not encrypted. You should probably "
                                     "encrypt it...".format(self.currency_code))
            else:
                self.logger.info("Unlocking {} wallet. Success: {}".
                                 format(self.currency_code, wallet))

    @rpc_conn
    def send_many(self, account, recip):
        """
        Runs the 'sendmany' rpc call on the given currency's rpc server
        """
        # Coercy account to a STR
        account = str(account)

        # Coerce all amounts to float
        for k, amount in recip.iteritems():
            recip[k] = float(amount)

        self.unlock_wallet()
        self.set_tx_fee(self.tx_fee)

        try:
            coin_tx = self.conn.sendmany(account, recip)
        except CoinRPCException as e:
            self.logger.error("Unable to send funds! CoinRPC returned an error "
                              "{}".format(e), exc_info=True)
            raise CoinRPCException
        else:
            self.logger.info("Successfully ran sendmany for {} to {}".
                             format(self.currency_code, recip))

            # Lookup the transaction we just created to grab fees
            tx = self.get_transaction(coin_tx)

            return coin_tx, tx

    @rpc_conn
    def get_balance(self, account=None):
        """
        Runs the 'getbalance' rpc call on the given currency's rpc server
        """
        if not account and not self.account:
            balance = self.conn.getbalance()
        else:
            balance = self.conn.getbalance(account or self.account)

        self.logger.info("Found {} {} balance in local wallet".
                         format(balance, self.currency_code))
        return balance

    @rpc_conn
    def get_block(self, block_hash):
        """
        Runs the 'getblock' rpc call on the given currency's rpc server
        """
        block_info = self.conn.getblock()
        self.logger.debug("For block {} received info:".format(block_hash,
                                                               block_info))

        try:
            assert 'height' in block_info
            assert 'confirmations' in block_info
            assert 'hash' in block_info
        except AssertionError:
            self.logger.warn('Got unexpected {} RPC getblock response. Got: {}'
                             .format(self.currency_code, pprint.pformat(block_info)),
                             exc_info=True)

        return block_info

    @rpc_conn
    def get_transactions_since(self, blockhash, confirms=1):
        """
        Runs the 'listsinceblock' rpc call on the given currency's rpc server
        and returns transactions

        listsinceblock sample output:

        { "lastblock" : "0000000004ba22e9f8cea2e843b34f7eeaa2c3b7004ddcf19bfd8af0215fc0cc",
          "transactions" : [ { "account" : "",
                "address" : "mzE6DJMHPghYpVg4GCurMbxSSXBfW1KCFH",
                "amount" : 1.0,
                "category" : "receive",
                "confirmations" : 0,
                "time" : 1399200157,
                "timereceived" : 1399200157,
                "txid" : "917248d57293a7fd3a88aa3a26026d2e4d6a1d4eef898519b20419f2339c265c",
                "walletconflicts" : [  ]
              } ]
        }
        """
        result = self.conn.listsinceblock(blockhash, confirms)

        self.logger.info("For {} block {} received : {}"
                         .format(self.currency_code, blockhash, result))

        transactions = []
        try:
            for tx_info in result['transactions']:
                tx = CoinTransaction.create(tx_info, self.currency_code)
                transactions.append(tx)
            lastblock = result['lastblock']
        except KeyError as e:
            self.logger.warn("Key error grabbing {} transactions since {}"
                             " Got: {}".format(blockhash, self.currency_code, e))
            raise CoinRPCException
        else:
            return transactions, lastblock

    @rpc_conn
    def get_received(self, address, confirms=1):
        """
        Runs the 'receivedbyaddress' rpc call on the given currency's rpc
        server and returns tx ids
        """
        results = self.conn.receivedbyaddress(address, confirms)
        self.logger.info("For {} block {} received : {}"
                         .format(self.currency_code, results))
        for result in results:
            try:
                transactions = result['txids']
            except KeyError as e:
                self.logger.warn("Key error with {} txids for {}. Got: {}"
                                 .format(self.currency_code, address, e))
                raise CoinRPCException
            else:
                return transactions

    @rpc_conn
    def get_block_count(self):
        """
        Runs the 'getblockcount' rpc call on the given currency's rpc server
        and returns the count
        """
        count = self.conn.getblockcount()
        self.logger.info("Got {} height for: {}"
                         .format(count, self.currency_code))
        return count

    @rpc_conn
    def get_block_hash(self, index):
        """
        Runs the 'getblockhash' rpc call on the given currency's rpc server
        and returns the height
        """
        hash = self.conn.getblockhash(index)
        self.logger.info("For {} height {} received : {}"
                         .format(self.currency_code, index, hash))
        return hash