bitcoinwatcher 2.4__py3-none-any.whl → 2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,9 @@ from ordipool.ordipool.mempoolio import Mempool
7
7
  from bitcoin.models.address_tx_data import AddressTxData
8
8
  from bitcoin.tx_extractors.bitcoin_rpc import BitcoinRPCAddressDataExtractor
9
9
  from bitcoin.tx_extractors.default import DefaultTxAddressDataExtractor
10
- from bitcoin.tx_extractors.mempool import MempoolTxAddressDataExtractor
11
10
  from bitcoin.tx_listener.abstract_tx_listener import AbstractTxListener
12
11
  from bitcoin.utils.constants import default_host
12
+ from bitcoin.utils.context_aware_logging import logger
13
13
 
14
14
 
15
15
  class AbstractAddressListener(AbstractTxListener, ABC):
@@ -18,7 +18,7 @@ class AbstractAddressListener(AbstractTxListener, ABC):
18
18
  base_url = f"http://{host}:3006/api"
19
19
  mempool = Mempool(base_url=base_url)
20
20
  default_tx_extractor = DefaultTxAddressDataExtractor()
21
- mempool_tx_extractor = BitcoinRPCAddressDataExtractor()
21
+ rpc_tx_extractor = BitcoinRPCAddressDataExtractor()
22
22
 
23
23
  def __init__(self, addresses_to_listen: {str}):
24
24
  self.addresses_to_listen = addresses_to_listen
@@ -44,9 +44,10 @@ class AbstractAddressListener(AbstractTxListener, ABC):
44
44
  return
45
45
  # get the address tx data from mempool for full details if any address matches
46
46
  try:
47
- address_tx_data = self.mempool_tx_extractor.extract(tx)
47
+ logger.info(f"Extracting rpc tx data")
48
+ address_tx_data = self.rpc_tx_extractor.extract(tx)
48
49
  except Exception as e:
49
- print(f"Error in getting mempool tx data, taking defaults: {e}")
50
+ logger.error(f"Error in getting rpc tx data, taking defaults",e, exc_info=True)
50
51
  address_tx_data = address_tx_data
51
52
 
52
53
  for address in addresses_for_events:
@@ -24,7 +24,7 @@ class AddressTxData:
24
24
  type: AddressTxType
25
25
  # figure out the way to get amount in vin
26
26
  _amount: int = 0
27
- tx_status: str = "unconfirmed"
27
+ is_confirmed: bool = False
28
28
  is_reveal_tx: bool = False
29
29
 
30
30
  def get_amount(self):
@@ -7,6 +7,7 @@ from bitcoin.address_listener.address_listener import AddressTxData
7
7
  from bitcoin.models.address_tx_data import AddressTxType
8
8
  from bitcoin.utils.bitcoin_rpc import BitcoinRPC
9
9
  from bitcoin.tx_extractors.abstract_extractor import AbstractTxAddressDataExtractor
10
+ from bitcoin.utils.context_aware_logging import logger
10
11
 
11
12
 
12
13
  class BitcoinRPCAddressDataExtractor(AbstractTxAddressDataExtractor):
@@ -28,7 +29,7 @@ class BitcoinRPCAddressDataExtractor(AbstractTxAddressDataExtractor):
28
29
  def get_address_tx_from_inputdata(self, tx_id, tx_status, input_data):
29
30
  address = input_data["scriptPubKey"]["address"]
30
31
  amount = int(input_data.get("value", 0).real * 100000000)
31
- return AddressTxData(tx_id=tx_id, tx_status=tx_status, address=address, _amount=amount,
32
+ return AddressTxData(tx_id=tx_id, is_confirmed=tx_status, address=address, _amount=amount,
32
33
  type=AddressTxType.INPUT)
33
34
 
34
35
  def extract(self, tx: Transaction) -> [AddressTxData]:
@@ -38,23 +39,24 @@ class BitcoinRPCAddressDataExtractor(AbstractTxAddressDataExtractor):
38
39
  inputs = tx.inputs
39
40
  # bulk get all the inputs from BitcoinRPC using thread pool
40
41
  inputs_data = self.fetch_all_inputs(inputs)
41
- tx_status = tx.status
42
+ is_confirmed = self.bitcoinrpc.is_confirmed(tx_id)
43
+ logger.info("Transaction is_confirmed: ", is_confirmed)
42
44
  for input in inputs:
43
45
  address = input.address
44
46
  amount = 0
45
47
  if len(inputs_data) > 0:
46
48
  input_data = inputs_data.pop(0)
47
- input_tx_data = self.get_address_tx_from_inputdata(tx_id, tx_status, input_data)
49
+ input_tx_data = self.get_address_tx_from_inputdata(tx_id, is_confirmed, input_data)
48
50
  address_tx_data.append(input_tx_data)
49
51
  else:
50
- address_tx_data.append(AddressTxData(tx_status=tx_status,
52
+ address_tx_data.append(AddressTxData(is_confirmed=is_confirmed,
51
53
  address=address,
52
54
  type=AddressTxType.INPUT,
53
55
  _amount=amount,
54
56
  tx_id=tx.txid))
55
57
  for output in outputs:
56
58
  amount = output.value
57
- address_tx_data.append(AddressTxData(tx_status=tx_status,
59
+ address_tx_data.append(AddressTxData(is_confirmed=is_confirmed,
58
60
  address=output.address,
59
61
  _amount=amount,
60
62
  type=AddressTxType.OUTPUT,
@@ -10,18 +10,18 @@ class DefaultTxAddressDataExtractor(AbstractTxAddressDataExtractor):
10
10
  outputs = tx.outputs
11
11
  address_tx_data = []
12
12
  inputs = tx.inputs
13
- tx_status = tx.status
13
+ is_confirmed = tx.status == "confirmed"
14
14
  for input in inputs:
15
15
  address = input.address
16
16
  amount = 0
17
- address_tx_data.append(AddressTxData(tx_status=tx_status,
17
+ address_tx_data.append(AddressTxData(is_confirmed=is_confirmed,
18
18
  address=address,
19
19
  type=AddressTxType.INPUT,
20
20
  _amount=amount,
21
21
  tx_id=tx.txid))
22
22
  for output in outputs:
23
23
  amount = output.value
24
- address_tx_data.append(AddressTxData(tx_status=tx_status,
24
+ address_tx_data.append(AddressTxData(is_confirmed=is_confirmed,
25
25
  address=output.address,
26
26
  _amount=amount,
27
27
  type=AddressTxType.OUTPUT,
@@ -5,6 +5,7 @@ from bitcoinlib.transactions import Transaction
5
5
 
6
6
  from bitcoin.tx_listener.abstract_tx_listener import AbstractTxListener
7
7
  from bitcoin.utils.constants import default_host, default_port
8
+ from bitcoin.utils.context_aware_logging import ctx_tx, logger
8
9
 
9
10
 
10
11
  class ZMQTXListener:
@@ -20,12 +21,13 @@ class ZMQTXListener:
20
21
 
21
22
  def start(self):
22
23
  connections = self.socket.connect(self.zmq_url)
23
- print(f"Connected to {connections}")
24
+ logger.info(f"Connected to {connections}")
24
25
  self.socket.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
25
26
  while True:
26
27
  topic, body, seq = self.socket.recv_multipart()
27
28
  try:
28
29
  tx = Transaction.parse(body, strict=False)
30
+ ctx_tx.set(tx.txid)
29
31
  self.tx_listener.on_tx(tx)
30
32
  except Exception as e:
31
33
  print(f"Error in parsing tx: {e}")
@@ -18,7 +18,6 @@ if __name__ == '__main__':
18
18
  txid = 'a6293e898b056fbea0329d071b1b237e4449ff464cdbc7a9ed8a770b97aafd4c'
19
19
  times = 1000
20
20
  # start = time.time()
21
- # print("mempool starting")
22
21
  # for i in range(times):
23
22
  # print(i)
24
23
  # mempool.get_transaction(txid)
@@ -29,5 +28,4 @@ if __name__ == '__main__':
29
28
  for i in range(times):
30
29
  tx = bitcoinrpc.get_transaction(txid)
31
30
  end = time.time()
32
- print(f"bitcoinrpc took: {end - start}")
33
31
 
@@ -3,6 +3,7 @@ import os
3
3
  from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
4
4
 
5
5
  from bitcoin.utils.constants import default_host
6
+ from bitcoin.utils.context_aware_logging import ctx_tx, ctx_tx_status, logger, get_logger
6
7
 
7
8
 
8
9
  class BitcoinRPC:
@@ -18,12 +19,21 @@ class BitcoinRPC:
18
19
  def get_transaction(self, txid: str) -> dict:
19
20
  return self.rpc_connection.getrawtransaction(txid, True)
20
21
 
22
+ def is_confirmed(self, txid: str) -> bool:
23
+ try:
24
+ self.rpc_connection.getmempoolentry(txid)
25
+ return False
26
+ except JSONRPCException as e:
27
+ return True
28
+
21
29
 
22
30
  if __name__ == '__main__':
31
+ logger = get_logger(__name__)
23
32
  rpc = BitcoinRPC()
24
- txid = '8dc035d6816cf61074d12148777b28d28aa2f76b0f45ceafd310a85b1518aab8'
33
+ txid = '686d025f16d9f20353665a9d865e575e3e4d14214f6f7045149a17dd6bf0fac6'
25
34
  try:
26
- transaction = rpc.get_transaction(txid)
27
- print(transaction)
35
+ ctx_tx.set('686d025f16d9f20353665a9d865e575e3e4d14214f6f7045149a17dd6bf0fac6')
36
+ ctx_tx_status.set('confirmed')
37
+ logger.info("Transaction is confirmed")
28
38
  except JSONRPCException as e:
29
- print(f"An error occurred: {e}")
39
+ print(f"An error occurred: {e}")
@@ -0,0 +1,54 @@
1
+ import logging
2
+ from contextvars import ContextVar
3
+ from datetime import datetime
4
+
5
+ import pytz
6
+
7
+ logger = logging.getLogger(__name__)
8
+ root = logging.getLogger()
9
+ root.setLevel(logging.INFO)
10
+
11
+ class TimezoneFormatter(logging.Formatter):
12
+ def __init__(self, fmt=None, datefmt=None, tz=None):
13
+ super().__init__(fmt, datefmt)
14
+ self.tz = tz
15
+
16
+ def formatTime(self, record, datefmt=None):
17
+ dt = datetime.fromtimestamp(record.created, self.tz)
18
+ if datefmt:
19
+ s = dt.strftime(datefmt)
20
+ else:
21
+ s = dt.isoformat()
22
+ return s
23
+
24
+
25
+ formatter = TimezoneFormatter(
26
+ ' %(asctime)s %(name)s txid=%(tx_id)s [%(module)s:%(lineno)d] %(message)s',
27
+ datefmt='%Y-%m-%d %H:%M:%S',
28
+ tz=pytz.timezone('Asia/Kolkata')
29
+ )
30
+
31
+
32
+ ctx_tx = ContextVar('tx_id', default='')
33
+ ctx_tx_status = ContextVar('tx_status', default='')
34
+
35
+ class TXContextFilter(logging.Filter):
36
+
37
+ def __init__(self):
38
+ super().__init__()
39
+
40
+ def filter(self, record):
41
+ tx_id = ctx_tx.get()
42
+ record.tx_id = tx_id
43
+ tx_status = ctx_tx_status.get()
44
+ record.tx_status = tx_status
45
+ return True
46
+
47
+ ch = logging.StreamHandler()
48
+ f = TXContextFilter()
49
+ ch.setFormatter(formatter)
50
+ ch.addFilter(f)
51
+ root.addHandler(ch)
52
+
53
+ def get_logger(name):
54
+ return logging.getLogger(name)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bitcoinwatcher
3
- Version: 2.4
3
+ Version: 2.6
4
4
  Summary: bitcoinwatcher is a Python library that implements a ZMQ subscriber and provides abstractions to build custom address watchers. This library is designed to make it easy for developers to monitor Bitcoin addresses and react to changes in their state.
5
5
  Author: twosatsmaxi
6
- License: Apache License
6
+ License: Apache License
7
7
  Version 2.0, January 2004
8
8
  http://www.apache.org/licenses/
9
9
 
@@ -217,17 +217,18 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
217
217
  Requires-Python: >=3.10
218
218
  Description-Content-Type: text/markdown
219
219
  License-File: LICENSE
220
- Requires-Dist: requests ~=2.31.0
221
- Requires-Dist: certifi ==2024.2.2
222
- Requires-Dist: charset-normalizer ==3.3.2
223
- Requires-Dist: idna ==3.7
224
- Requires-Dist: pyzmq ==25.1.2
225
- Requires-Dist: requests ==2.31.0
226
- Requires-Dist: six ==1.16.0
227
- Requires-Dist: urllib3 ==2.2.1
228
- Requires-Dist: bitcoinlib ==0.6.14
229
- Requires-Dist: ordipool ==1.2.0
230
- Requires-Dist: python-bitcoinrpc ==1.0
220
+ Requires-Dist: requests~=2.31.0
221
+ Requires-Dist: certifi==2024.2.2
222
+ Requires-Dist: charset-normalizer==3.3.2
223
+ Requires-Dist: idna==3.7
224
+ Requires-Dist: pyzmq==25.1.2
225
+ Requires-Dist: requests==2.31.0
226
+ Requires-Dist: urllib3==2.2.1
227
+ Requires-Dist: bitcoinlib==0.7.0
228
+ Requires-Dist: ordipool==1.2.0
229
+ Requires-Dist: pyzmq==25.1.2
230
+ Requires-Dist: python-bitcoinrpc==1.0
231
+ Requires-Dist: pytz~=2024.2
231
232
 
232
233
  # bitcoinwatcher
233
234
 
@@ -1,9 +1,9 @@
1
1
  bitcoin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  bitcoin/address_listener/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- bitcoin/address_listener/address_listener.py,sha256=32gDEYpYTv53nD0TKLYyrIzHpImYBzzT3XdCoGUD1fI,2354
3
+ bitcoin/address_listener/address_listener.py,sha256=TkSB51U0WTfdbBnFFMXi5EK673USjvdQoytx2vWKKMk,2395
4
4
  bitcoin/address_listener/simple_address_listener.py,sha256=WG8eTrd3AdkrIs1TAGRsXe_erAtE_Ep8hidZDksGk8g,1625
5
5
  bitcoin/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- bitcoin/models/address_tx_data.py,sha256=0zIleuEHern42zk1Pp7IteR5jQqVdAM1l4Gvy6YzGe0,754
6
+ bitcoin/models/address_tx_data.py,sha256=KSSCzAjYxLEgXIELVSsnSEhgY65xi0K3HUjoamVV_LQ,750
7
7
  bitcoin/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  bitcoin/tests/test_address_listener.py,sha256=USK3BMGnRsTBmCXcbnC9WkX6kF-W_sHt7RKARv39IDg,3125
9
9
  bitcoin/tests/test_bitcoin_rpc.py,sha256=Xt9Mh71YQqE_gskgLMfPLs0J0YcCT7z5UimT42LOris,4137
@@ -12,20 +12,21 @@ bitcoin/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
12
12
  bitcoin/tests/data/transactions.py,sha256=qIX9seGYguQ1lvLOCF8oe3NQhMJZP0SsBmz1iFbszhg,396279
13
13
  bitcoin/tx_extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  bitcoin/tx_extractors/abstract_extractor.py,sha256=YLnuqc3jva15NpOX8xV4oB5PEXc6rXbG8Lx0cBXW66s,291
15
- bitcoin/tx_extractors/bitcoin_rpc.py,sha256=PFyX5DfJspj9AM3KCVJG91dYxTtb73PW10JKzHDljww,2800
16
- bitcoin/tx_extractors/default.py,sha256=ZW9bJjq4-gvp-jJzNTrTFRRTReb4mOO0OmpTnDenhBQ,1377
15
+ bitcoin/tx_extractors/bitcoin_rpc.py,sha256=btxz7jr95SG8IuTg4klRT5OMvyVm5B3pO_-e50_MEiY,2966
16
+ bitcoin/tx_extractors/default.py,sha256=Xd09vldoFIlZo2g25KQeWbPKw7Cyj6vp40AKeDn3nJM,1407
17
17
  bitcoin/tx_extractors/mempool.py,sha256=mPPY1GOvBmZWn7UVP2sUaXtfcyeTCj-LmRDl2F4WhC8,2090
18
18
  bitcoin/tx_listener/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  bitcoin/tx_listener/abstract_tx_listener.py,sha256=rGp9na-aOSl_aPaIqf44RTZaFF3SvrxIhZBujTnWqao,189
20
- bitcoin/tx_listener/zmq_listener.py,sha256=V4-WIt5PYJaYoipn0rYhcFfg1BBYxqqQGucKhGXTmkg,1110
20
+ bitcoin/tx_listener/zmq_listener.py,sha256=HCLtR6s6kBH1IP8WXJA9UfnR8kczwk7Mdtexn3r1yoI,1215
21
21
  bitcoin/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- bitcoin/utils/benchmark.py,sha256=kaTTlo6k_5uDVIt8FMeY4nH7ZxxsoWUiMsZgHfpWsMo,894
23
- bitcoin/utils/bitcoin_rpc.py,sha256=yEHS8BPI_Y4uLFL1ygUti8OI68JW67d_c1vshjEfQbA,932
22
+ bitcoin/utils/benchmark.py,sha256=HXZy9vU34MKrBoVjx3y982TFEOuSmOz5HWf5LcFwaZA,817
23
+ bitcoin/utils/bitcoin_rpc.py,sha256=wyTIahgdSyiR1ryPi75n33ERuKQK630YTO9hNs_MML4,1358
24
24
  bitcoin/utils/bitcoin_utils.py,sha256=mrnRPqUa2U2EMKu7rrPV_bW1sL2CJUfbAom0Zdamydk,631
25
25
  bitcoin/utils/constants.py,sha256=irZLlArgica2VckyckEYxH5D5KjvdF52dtBMWswqw8k,52
26
+ bitcoin/utils/context_aware_logging.py,sha256=qPBhi44Hks_LaiW516T9oGqY1WIbUQqHOR1KckG5l1s,1307
26
27
  bitcoin/utils/inscription_utils.py,sha256=8QbOJ1o1n1bMFsPREGLzwFjnGzfuARgJCPr6ORhP44o,193
27
- bitcoinwatcher-2.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
- bitcoinwatcher-2.4.dist-info/METADATA,sha256=gaTSlQkwg__aqoHgdWVx2e7VCyzYtJv88VVVo_FokvU,14938
29
- bitcoinwatcher-2.4.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
30
- bitcoinwatcher-2.4.dist-info/top_level.txt,sha256=YdUgzLdCiMlrwaKyDqHA1acEd23QFko5bv7D6nBANJ0,8
31
- bitcoinwatcher-2.4.dist-info/RECORD,,
28
+ bitcoinwatcher-2.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
+ bitcoinwatcher-2.6.dist-info/METADATA,sha256=Oyi_L_ZKMkkhUdbDAFOcES7u6qB5Mcjbnz02iIv7FcU,14989
30
+ bitcoinwatcher-2.6.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
31
+ bitcoinwatcher-2.6.dist-info/top_level.txt,sha256=YdUgzLdCiMlrwaKyDqHA1acEd23QFko5bv7D6nBANJ0,8
32
+ bitcoinwatcher-2.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5