Skip to content

Commit

Permalink
Merge pull request #426 from 1200wd/service-class-stats
Browse files Browse the repository at this point in the history
Service class stats
  • Loading branch information
mccwdev authored Dec 9, 2024
2 parents d252f9f + 40130f9 commit 9859db1
Showing 1 changed file with 32 additions and 11 deletions.
43 changes: 32 additions & 11 deletions bitcoinlib/services/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Service(object):

def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, providers=None,
timeout=TIMEOUT_REQUESTS, cache_uri=None, ignore_priority=False, exclude_providers=None,
max_errors=SERVICE_MAX_ERRORS, strict=True, wallet_name=None):
max_errors=SERVICE_MAX_ERRORS, strict=True, wallet_name=None, provider_name=None):
"""
Create a service object for the specified network. By default, the object connect to 1 service provider, but you
can specify a list of providers or a minimum or maximum number of providers.
Expand All @@ -80,6 +80,8 @@ def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, pr
:type strict: bool
:param wallet_name: Name of wallet if connecting to bitcoin node
:type wallet_name: str
:param provider_name: Name of specific provider to connect to. Note this is different from the providers list argument: the lists mention a type of provider such as 'blockbook' or 'bcoin', the provider name is a key in providers.json list such as 'bcoin.testnet.localhost'.
:type provider_name: str
"""

Expand Down Expand Up @@ -111,11 +113,19 @@ def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, pr
raise ServiceError("Provider '%s' not found in provider definitions" % p)

self.providers = {}
for p in self.providers_defined:
if (self.providers_defined[p]['network'] == network or self.providers_defined[p]['network'] == '') and \
(not providers or self.providers_defined[p]['provider'] in providers):
self.providers.update({p: self.providers_defined[p]})
exclude_providers_keys = {pi: self.providers[pi]['provider'] for pi in self.providers if self.providers[pi]['provider'] in exclude_providers}.keys()
if provider_name:
if provider_name not in self.providers_defined:
raise ServiceError("Provider with name '%s' not found in provider definitions" % provider_name)
if self.providers_defined[provider_name]['network'] != self.network:
raise ServiceError("Network from provider '%s' is different than Service network" % provider_name)
self.providers.update({provider_name: self.providers_defined[provider_name]})
else:
for p in self.providers_defined:
if (self.providers_defined[p]['network'] == network or self.providers_defined[p]['network'] == '') and \
(not providers or self.providers_defined[p]['provider'] in providers):
self.providers.update({p: self.providers_defined[p]})
exclude_providers_keys = {pi: self.providers[pi]['provider'] for
pi in self.providers if self.providers[pi]['provider'] in exclude_providers}.keys()
for provider_key in exclude_providers_keys:
del(self.providers[provider_key])

Expand All @@ -142,6 +152,7 @@ def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, pr
self.results_cache_n = 0
self.ignore_priority = ignore_priority
self.strict = strict
self.execution_time = None
if self.min_providers > 1:
self._blockcount = Service(network=network, cache_uri=cache_uri, providers=providers,
exclude_providers=exclude_providers, timeout=timeout).blockcount()
Expand All @@ -153,6 +164,7 @@ def _reset_results(self):
self.errors = {}
self.complete = None
self.resultcount = 0
self.execution_time = None

def _provider_execute(self, method, *arguments):
self._reset_results()
Expand All @@ -161,6 +173,7 @@ def _provider_execute(self, method, *arguments):
if self.ignore_priority:
random.shuffle(provider_lst)

start_time = datetime.now()
for sp in provider_lst:
if self.resultcount >= self.max_providers:
break
Expand Down Expand Up @@ -220,6 +233,7 @@ def _provider_execute(self, method, *arguments):
if self.resultcount >= self.max_providers:
break

self.execution_time = (datetime.now() - start_time).total_seconds() * 1000
if not self.resultcount:
raise ServiceError("No successful response from any serviceprovider: %s" % list(self.providers.keys()))
return list(self.results.values())[0]
Expand Down Expand Up @@ -496,11 +510,18 @@ def blockcount(self):
current_timestamp = time.time()
if self._blockcount_update < current_timestamp - BLOCK_COUNT_CACHE_TIME:
new_count = self._provider_execute('blockcount')
if not self._blockcount or (new_count and new_count > self._blockcount):
if last_cache_blockcount > new_count:
_logger.warning(f"New block count ({new_count}) is lower than block count in cache "
f"({last_cache_blockcount}). Will try to find provider consensus")
blockcounts = [last_cache_blockcount]
for _ in range(5):
blockcounts.append(self._provider_execute('blockcount'))
# return third last blockcount in list, assume last 2 and first 3 could be wrong
self._blockcount = sorted(blockcounts)[-2]
self._blockcount_update = current_timestamp
elif not self._blockcount or (new_count and new_count > self._blockcount):
self._blockcount = new_count
self._blockcount_update = current_timestamp
if last_cache_blockcount > self._blockcount:
return last_cache_blockcount
# Store result in cache
if len(self.results) and list(self.results.keys())[0] != 'caching':
self.cache.store_blockcount(self._blockcount)
Expand Down Expand Up @@ -593,7 +614,7 @@ def mempool(self, txid=''):
"""
Get list of all transaction IDs in the current mempool
A full list of transactions ID's will only be returned if a bcoin or bitcoind client is available. Otherwise
A full list of transactions ID's will only be returned if a bcoin or bitcoind client is available. Otherwise,
specify the txid option to verify if a transaction is added to the mempool.
:param txid: Check if transaction with this hash exists in memory pool
Expand All @@ -605,7 +626,7 @@ def mempool(self, txid=''):

def getcacheaddressinfo(self, address):
"""
Get address information from cache. I.e. balance, number of transactions, number of utox's, etc
Get address information from cache. I.e. balance, number of transactions, number of utxo's, etc
Cache will only be filled after all transactions for a specific address are retrieved (with gettransactions ie)
Expand Down

0 comments on commit 9859db1

Please sign in to comment.