Skip to content

Commit 0a98e28

Browse files
committedOct 13, 2015
added automatic service selection with one allowed failure
1 parent f619c9f commit 0a98e28

File tree

5 files changed

+76
-93
lines changed

5 files changed

+76
-93
lines changed
 

‎btctxstore/api.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
class BtcTxStore(): # TODO use apigen when ported to python 3
2020
"""Bitcoin nulldata output io library."""
2121

22-
def __init__(self, testnet=False, dryrun=False):
22+
def __init__(self, testnet=False, dryrun=False, service="automatic"):
2323
self.testnet = deserialize.flag(testnet)
2424
self.dryrun = deserialize.flag(dryrun)
25-
self.service = services.select("blockexplorer", testnet=testnet,
25+
self.service = services.select(service, testnet=testnet,
2626
dryrun=dryrun)
2727

2828
###########

‎btctxstore/services/__init__.py

+12-14
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55

66
from btctxstore.services.insight import Insight
77
from btctxstore.services.blockexplorer import BlockExplorer
8+
from btctxstore.services.automatic import Automatic
89

9-
10-
#from btctxstore.services.blockchaininfo import BlockchainInfo
11-
# blockchain.info removed because it doesnt accept transaction with OP_RETURN
12-
10+
_all = {
11+
"insight": Insight,
12+
"blockexplorer": BlockExplorer,
13+
}
1314

1415
def select(name, testnet=False, dryrun=False):
15-
service_dict = { # { "name" : (class, testnet_supported), ... }
16-
"insight": (Insight, True),
17-
"blockexplorer": (BlockExplorer, True),
18-
#"blockchain.info": (BlockchainInfo, False),
19-
}
20-
service_class, testnet_supported = service_dict.get(name, (None, False))
21-
if service_class is None:
16+
service_class = _all.get(name)
17+
if service_class is not None:
18+
return service_class(testnet=testnet, dryrun=dryrun)
19+
if name != "automatic":
2220
raise Exception("Service {0} not found!".format(name))
23-
if testnet and not testnet_supported:
24-
raise Exception("Service {0} does not support testnet!".format(name))
25-
return service_class(testnet=testnet, dryrun=dryrun)
21+
return Automatic(testnet=testnet, dryrun=dryrun,
22+
service_classes=_all.values())
23+

‎btctxstore/services/automatic.py

+62-9
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,73 @@
33
# License: MIT (see LICENSE file)
44

55

6+
import random
7+
import logging
8+
from btctxstore.services.interface import BlockchainService
69

7-
class Insight(BlockchainService):
810

9-
def __init__(self, testnet=False, dryrun=False):
10-
super(Insight, self).__init__(testnet=testnet, dryrun=dryrun)
11+
_log = logging.getLogger(__name__)
12+
13+
14+
class Automatic(BlockchainService):
15+
16+
def __init__(self, testnet=False, dryrun=False, service_classes=[]):
17+
super(Automatic, self).__init__(testnet=testnet, dryrun=dryrun)
18+
if not service_classes:
19+
raise ValueError()
20+
self.services = []
21+
for service_class in service_classes:
22+
self.services.append(service_class(testnet=testnet, dryrun=dryrun))
23+
24+
def _select_service(self):
25+
return random.choice(self.services)
26+
27+
def _select_other_service(self, service):
28+
for other_service in self.services:
29+
if other_service is not service:
30+
return other_service
31+
raise Exception("Not enough services!")
1132

1233
def get_tx(self, txid):
13-
"""TODO doc string"""
14-
raise NotImplementedError()
34+
service = self._select_service()
35+
try:
36+
return service.get_tx(txid)
37+
except Exception as e:
38+
# try only once with another service
39+
# if two independant services fail something is wrong
40+
# there are also only two working services right now ...
41+
name = service.__class__.__name__
42+
msg = "Service call to {0} failed: {1}"
43+
_log.error(msg.format(name, repr(e)))
44+
other_service = self._select_other_service(service)
45+
return other_service.get_tx(txid)
1546

1647
def send_tx(self, tx):
17-
"""TODO doc string"""
18-
raise NotImplementedError()
48+
service = self._select_service()
49+
try:
50+
return service.send_tx(tx)
51+
except Exception as e:
52+
# try only once with another service
53+
# if two independant services fail something is wrong
54+
# there are also only two working services right now ...
55+
name = service.__class__.__name__
56+
msg = "Service call to {0} failed: {1}"
57+
_log.error(msg.format(name, repr(e)))
58+
other_service = self._select_other_service(service)
59+
return other_service.send_tx(tx)
60+
61+
# TODO override spendables_for_addresses in case implementation optimizes
1962

2063
def spendables_for_address(self, bitcoin_address):
21-
"""TODO doc string"""
22-
raise NotImplementedError()
64+
service = self._select_service()
65+
try:
66+
return service.spendables_for_address(bitcoin_address)
67+
except Exception as e:
68+
# try only once with another service
69+
# if two independant services fail something is wrong
70+
# there are also only two working services right now ...
71+
name = service.__class__.__name__
72+
msg = "Service call to {0} failed: {1}"
73+
_log.error(msg.format(name, repr(e)))
74+
other_service = self._select_other_service(service)
75+
return other_service.spendables_for_address(bitcoin_address)

‎btctxstore/services/blockchaininfo.py

-67
This file was deleted.

‎tests/api/other.py

-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ def test_getutxos(self):
141141
address = fixtures["wallet"]["address"]
142142
expected = fixtures["getutxos"]["expected"]
143143
result = self.api.retrieve_utxos([address])
144-
print("RESULT", result)
145144
self.assertEqual(result, expected)
146145

147146

0 commit comments

Comments
 (0)
Please sign in to comment.