bitcoinwatcher 1.0.1__tar.gz → 1.1.0__tar.gz

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.
Files changed (29) hide show
  1. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/PKG-INFO +2 -1
  2. bitcoinwatcher-1.1.0/bitcoin/address_listener/address_listener.py +51 -0
  3. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/address_listener/simple_address_listener.py +3 -1
  4. bitcoinwatcher-1.1.0/bitcoin/models/address_tx_data.py +36 -0
  5. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/tests/test_address_listener.py +4 -2
  6. bitcoinwatcher-1.1.0/bitcoin/utils/__init__.py +0 -0
  7. bitcoinwatcher-1.1.0/bitcoin/utils/benchmark.py +33 -0
  8. bitcoinwatcher-1.1.0/bitcoin/utils/bitcoin_rpc.py +31 -0
  9. bitcoinwatcher-1.1.0/bitcoin/utils/tx_address_data_extractor.py +70 -0
  10. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoinwatcher.egg-info/PKG-INFO +2 -1
  11. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoinwatcher.egg-info/SOURCES.txt +5 -0
  12. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoinwatcher.egg-info/requires.txt +1 -0
  13. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/pyproject.toml +3 -2
  14. bitcoinwatcher-1.0.1/bitcoin/address_listener/address_listener.py +0 -81
  15. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/LICENSE +0 -0
  16. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/README.md +0 -0
  17. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/__init__.py +0 -0
  18. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/address_listener/__init__.py +0 -0
  19. {bitcoinwatcher-1.0.1/bitcoin/tests → bitcoinwatcher-1.1.0/bitcoin/models}/__init__.py +0 -0
  20. {bitcoinwatcher-1.0.1/bitcoin/tx_listener → bitcoinwatcher-1.1.0/bitcoin/tests}/__init__.py +0 -0
  21. {bitcoinwatcher-1.0.1/bitcoin/utils → bitcoinwatcher-1.1.0/bitcoin/tx_listener}/__init__.py +0 -0
  22. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/tx_listener/abstract_tx_listener.py +0 -0
  23. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/tx_listener/zmq_listener.py +0 -0
  24. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/utils/bitcoin_utils.py +0 -0
  25. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/utils/constants.py +0 -0
  26. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoin/utils/inscription_utils.py +0 -0
  27. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoinwatcher.egg-info/dependency_links.txt +0 -0
  28. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/bitcoinwatcher.egg-info/top_level.txt +0 -0
  29. {bitcoinwatcher-1.0.1 → bitcoinwatcher-1.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bitcoinwatcher
3
- Version: 1.0.1
3
+ Version: 1.1.0
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
6
  License: Apache License
@@ -227,6 +227,7 @@ Requires-Dist: six==1.16.0
227
227
  Requires-Dist: urllib3==2.2.1
228
228
  Requires-Dist: bitcoinlib==0.6.14
229
229
  Requires-Dist: ordipool==1.2.0
230
+ Requires-Dist: python-bitcoinrpc==1.0
230
231
 
231
232
  # bitcoinwatcher
232
233
 
@@ -0,0 +1,51 @@
1
+ import os
2
+ from abc import ABC, abstractmethod
3
+
4
+ from bitcoinlib.transactions import Transaction
5
+ from ordipool.ordipool.mempoolio import Mempool
6
+
7
+ from bitcoin.models.address_tx_data import AddressTxData
8
+ from bitcoin.tx_listener.abstract_tx_listener import AbstractTxListener
9
+ from bitcoin.utils.constants import default_host
10
+ from bitcoin.utils.tx_address_data_extractor import DefaultTxAddressDataExtractor, MempoolTxAddressDataExtractor
11
+
12
+
13
+ class AbstractAddressListener(AbstractTxListener, ABC):
14
+ DECIMAL_SCALE = 5
15
+ host = os.environ.get("RPC_HOST", default_host)
16
+ base_url = f"http://{host}:3006/api"
17
+ mempool = Mempool(base_url=base_url)
18
+ default_tx_extractor = DefaultTxAddressDataExtractor()
19
+ mempool_tx_extractor = MempoolTxAddressDataExtractor(mempool)
20
+
21
+ def __init__(self, addresses_to_listen: {str}):
22
+ self.addresses_to_listen = addresses_to_listen
23
+
24
+ @abstractmethod
25
+ def consume(self, subscribed_address, address_tx_data: [AddressTxData]):
26
+ pass
27
+
28
+ def filter_address_tx_data(self, address_tx_data: [AddressTxData]) -> [str]:
29
+ filtered_address_tx_data = list(filter(lambda x: x.address in self.addresses_to_listen and x.address != "",
30
+ address_tx_data))
31
+ # get all address
32
+ return list(set((map(lambda x: x.address, filtered_address_tx_data))))
33
+
34
+ def on_tx(self, tx: Transaction):
35
+ # get all address in the inputs and outputs along with the amount
36
+ if tx.coinbase:
37
+ return
38
+ address_tx_data = self.default_tx_extractor.extract(tx)
39
+ # filter the address we are interested in
40
+ addresses_for_events = self.filter_address_tx_data(address_tx_data)
41
+ if len(addresses_for_events) == 0:
42
+ return
43
+ # get the address tx data from mempool for full details if any address matches
44
+ try:
45
+ address_tx_data = self.mempool_tx_extractor.extract(tx)
46
+ except Exception as e:
47
+ print(f"Error in getting mempool tx data, taking defaults: {e}")
48
+ address_tx_data = address_tx_data
49
+
50
+ for address in addresses_for_events:
51
+ self.consume(subscribed_address=address, address_tx_data=address_tx_data)
@@ -1,4 +1,5 @@
1
- from bitcoin.address_listener.address_listener import AbstractAddressListener, AddressTxData, AddressTxType
1
+ from bitcoin.address_listener.address_listener import AbstractAddressListener, AddressTxData
2
+ from bitcoin.models.address_tx_data import AddressTxType
2
3
  from bitcoin.tx_listener.zmq_listener import ZMQTXListener
3
4
 
4
5
 
@@ -11,6 +12,7 @@ class SimpleAddressListener(AbstractAddressListener):
11
12
  address_tx_data))
12
13
  total_amount_in_input = sum(map(lambda x: x.amount_in_btc(), all_input))
13
14
  # scale ito 4 decimal places
15
+ print("Transaction status: ", address_tx_data[0].tx_status)
14
16
  total_amount_in_output = round(total_amount_in_output, self.DECIMAL_SCALE)
15
17
  print(f"Address {subscribed_address} received {total_amount_in_output} BTC in tx {address_tx_data[0].tx_id}")
16
18
  print(f"Address {subscribed_address} spent {total_amount_in_input} BTC in tx {address_tx_data[0].tx_id}")
@@ -0,0 +1,36 @@
1
+ import dataclasses
2
+ from enum import Enum
3
+
4
+
5
+ class AddressTxType(Enum):
6
+ INPUT = "input"
7
+ OUTPUT = "output"
8
+
9
+
10
+ class AddressTxSource(Enum):
11
+ MEMPOOL = "mempool"
12
+ DEFAULT = "default"
13
+
14
+
15
+ class TransactionStatus(Enum):
16
+ UNCONFIRMED = "unconfirmed"
17
+ CONFIRMED = "confirmed"
18
+
19
+
20
+ @dataclasses.dataclass
21
+ class AddressTxData:
22
+ tx_id: str
23
+ address: str
24
+ type: AddressTxType
25
+ # figure out the way to get amount in vin
26
+ _amount: int = 0
27
+ tx_status: str = "unconfirmed"
28
+
29
+
30
+ def get_amount(self):
31
+ return self._amount
32
+
33
+ def amount_in_btc(self):
34
+ if self.type == AddressTxType.INPUT:
35
+ return "Amount is not supported for input type, yet"
36
+ return self._amount / 100000000
@@ -1,10 +1,12 @@
1
1
  from unittest import TestCase
2
2
 
3
- from bitcoin.address_listener.address_listener import AbstractAddressListener, AddressTxData, AddressTxType
3
+ from bitcoin.address_listener.address_listener import AbstractAddressListener, AddressTxData
4
+ from bitcoin.models.address_tx_data import AddressTxType
4
5
 
5
6
 
6
7
  class DummyAddressListener(AbstractAddressListener):
7
- def consume(self, address_tx_data):
8
+
9
+ def consume(self, subscribed_address, address_tx_data: [AddressTxData]):
8
10
  pass
9
11
 
10
12
  def __init__(self, address_to_listen: [str]):
File without changes
@@ -0,0 +1,33 @@
1
+ # benchmark between mempool and bitcoinrcp get transaction info
2
+ import os
3
+ import time
4
+ import requests
5
+ from ordipool.ordipool.mempoolio import Mempool
6
+
7
+ from bitcoin.utils.bitcoin_rpc import BitcoinRPC
8
+ from bitcoin.utils.constants import default_host
9
+
10
+ host = os.environ.get("RPC_HOST", default_host)
11
+ base_url = f"http://{host}:3006/api"
12
+
13
+ mempool = Mempool(base_url)
14
+ bitcoinrpc = BitcoinRPC()
15
+
16
+ if __name__ == '__main__':
17
+
18
+ txid = 'a6293e898b056fbea0329d071b1b237e4449ff464cdbc7a9ed8a770b97aafd4c'
19
+ times = 1000
20
+ start = time.time()
21
+ print("mempool starting")
22
+ for i in range(times):
23
+ print(i)
24
+ mempool.get_transaction(txid)
25
+ end = time.time()
26
+ print(f"mempool took: {end - start}")
27
+
28
+ start = time.time()
29
+ for i in range(times):
30
+ bitcoinrpc.get_transaction(txid)
31
+ end = time.time()
32
+ print(f"bitcoinrpc took: {end - start}")
33
+
@@ -0,0 +1,31 @@
1
+ import os
2
+
3
+ from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
4
+
5
+ from bitcoin.utils.constants import default_host
6
+
7
+
8
+ class BitcoinRPC:
9
+ rpc_user = os.environ.get("RPC_USER")
10
+ rpc_password = os.environ.get("RPC_PASSWORD")
11
+ rpc_host = os.environ.get("RPC_HOST", default_host)
12
+ rpc_port = os.environ.get("RPC_PORT", 8332)
13
+ rpc_connection: AuthServiceProxy
14
+
15
+ def __init__(self):
16
+ rpc_string = f"http://{self.rpc_user}:{self.rpc_password}@{self.rpc_host}:{self.rpc_port}"
17
+ print("rpc_string: ", rpc_string)
18
+ self.rpc_connection = AuthServiceProxy(rpc_string)
19
+
20
+ def get_transaction(self, txid: str) -> dict:
21
+ return self.rpc_connection.getrawtransaction(txid, True)
22
+
23
+
24
+ if __name__ == '__main__':
25
+ rpc = BitcoinRPC()
26
+ txid = 'a6293e898b056fbea0329d071b1b237e4449ff464cdbc7a9ed8a770b97aafd4c'
27
+ try:
28
+ transaction = rpc.get_transaction(txid)
29
+ print(transaction)
30
+ except JSONRPCException as e:
31
+ print(f"An error occurred: {e}")
@@ -0,0 +1,70 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from bitcoinlib.transactions import Transaction
4
+ from ordipool.ordipool.mempoolio import Mempool
5
+
6
+ from bitcoin.address_listener.address_listener import AddressTxData
7
+ from bitcoin.models.address_tx_data import AddressTxType, TransactionStatus
8
+
9
+
10
+ class AbstractTxAddressDataExtractor(ABC):
11
+ @abstractmethod
12
+ def extract(self, tx: Transaction) -> [AddressTxData]:
13
+ pass
14
+
15
+
16
+ class MempoolTxAddressDataExtractor(AbstractTxAddressDataExtractor):
17
+ mempool: Mempool
18
+
19
+ def __init__(self, mempool: Mempool):
20
+ self.mempool = mempool
21
+
22
+ def extract(self, tx: Transaction) -> [AddressTxData]:
23
+ outputs = tx.outputs
24
+ tx_id = tx.txid
25
+ tx = self.mempool.get_transaction(tx_id)
26
+ tx_status = TransactionStatus.CONFIRMED if tx.confirmed else TransactionStatus.UNCONFIRMED
27
+ address_tx_data = []
28
+ # getting inputs data separately from mempool
29
+ # as current library doesn't provide rich data like previous outputs and its value
30
+ for vin in tx.vins:
31
+ address = vin.prev_out.address
32
+ amount = vin.prev_out.value
33
+ address_tx_data.append(AddressTxData(tx_status=tx_status.value,
34
+ address=address,
35
+ type=AddressTxType.INPUT,
36
+ _amount=amount,
37
+ tx_id=tx_id))
38
+ for output in outputs:
39
+ amount = output.value
40
+ address_tx_data.append(AddressTxData(tx_status=tx_status.value,
41
+ address=output.address,
42
+ _amount=amount,
43
+ type=AddressTxType.OUTPUT,
44
+ tx_id=tx_id))
45
+
46
+ return address_tx_data
47
+
48
+
49
+ class DefaultTxAddressDataExtractor(AbstractTxAddressDataExtractor):
50
+ def extract(self, tx: Transaction) -> [AddressTxData]:
51
+ outputs = tx.outputs
52
+ address_tx_data = []
53
+ inputs = tx.inputs
54
+ tx_status = tx.status
55
+ for input in inputs:
56
+ address = input.address
57
+ amount = 0
58
+ address_tx_data.append(AddressTxData(tx_status=tx_status,
59
+ address=address,
60
+ type=AddressTxType.INPUT,
61
+ _amount=amount,
62
+ tx_id=tx.txid))
63
+ for output in outputs:
64
+ amount = output.value
65
+ address_tx_data.append(AddressTxData(tx_status=tx_status,
66
+ address=output.address,
67
+ _amount=amount,
68
+ type=AddressTxType.OUTPUT,
69
+ tx_id=tx.txid))
70
+ return address_tx_data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bitcoinwatcher
3
- Version: 1.0.1
3
+ Version: 1.1.0
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
6
  License: Apache License
@@ -227,6 +227,7 @@ Requires-Dist: six==1.16.0
227
227
  Requires-Dist: urllib3==2.2.1
228
228
  Requires-Dist: bitcoinlib==0.6.14
229
229
  Requires-Dist: ordipool==1.2.0
230
+ Requires-Dist: python-bitcoinrpc==1.0
230
231
 
231
232
  # bitcoinwatcher
232
233
 
@@ -5,15 +5,20 @@ bitcoin/__init__.py
5
5
  bitcoin/address_listener/__init__.py
6
6
  bitcoin/address_listener/address_listener.py
7
7
  bitcoin/address_listener/simple_address_listener.py
8
+ bitcoin/models/__init__.py
9
+ bitcoin/models/address_tx_data.py
8
10
  bitcoin/tests/__init__.py
9
11
  bitcoin/tests/test_address_listener.py
10
12
  bitcoin/tx_listener/__init__.py
11
13
  bitcoin/tx_listener/abstract_tx_listener.py
12
14
  bitcoin/tx_listener/zmq_listener.py
13
15
  bitcoin/utils/__init__.py
16
+ bitcoin/utils/benchmark.py
17
+ bitcoin/utils/bitcoin_rpc.py
14
18
  bitcoin/utils/bitcoin_utils.py
15
19
  bitcoin/utils/constants.py
16
20
  bitcoin/utils/inscription_utils.py
21
+ bitcoin/utils/tx_address_data_extractor.py
17
22
  bitcoinwatcher.egg-info/PKG-INFO
18
23
  bitcoinwatcher.egg-info/SOURCES.txt
19
24
  bitcoinwatcher.egg-info/dependency_links.txt
@@ -8,3 +8,4 @@ six==1.16.0
8
8
  urllib3==2.2.1
9
9
  bitcoinlib==0.6.14
10
10
  ordipool==1.2.0
11
+ python-bitcoinrpc==1.0
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "bitcoinwatcher"
8
- version = "1.0.1"
8
+ version = "1.1.0"
9
9
  description = "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."
10
10
  readme = "README.md"
11
11
  authors = [{name = "twosatsmaxi"}]
@@ -32,7 +32,8 @@ dependencies = [
32
32
  "six==1.16.0",
33
33
  "urllib3==2.2.1",
34
34
  "bitcoinlib==0.6.14",
35
- "ordipool==1.2.0"
35
+ "ordipool==1.2.0",
36
+ "python-bitcoinrpc==1.0",
36
37
  ]
37
38
  requires-python = ">=3.10"
38
39
  [project.urls]
@@ -1,81 +0,0 @@
1
- import dataclasses
2
- import os
3
- from abc import ABC, abstractmethod
4
- from enum import Enum
5
-
6
- from bitcoinlib.transactions import Transaction
7
- from ordipool.ordipool.mempoolio import Mempool
8
-
9
- from bitcoin.tx_listener.abstract_tx_listener import AbstractTxListener
10
- from bitcoin.utils.constants import default_host
11
-
12
-
13
- class AddressTxType(Enum):
14
- INPUT = "input"
15
- OUTPUT = "output"
16
-
17
-
18
- @dataclasses.dataclass
19
- class AddressTxData:
20
- tx_id: str
21
- address: str
22
- type: AddressTxType
23
- # figure out the way to get amount in vin
24
- _amount: int = 0
25
-
26
- def get_amount(self):
27
- return self._amount
28
-
29
- def amount_in_btc(self):
30
- if self.type == AddressTxType.INPUT:
31
- return "Amount is not supported for input type, yet"
32
- return self._amount / 100000000
33
-
34
-
35
- class AbstractAddressListener(AbstractTxListener, ABC):
36
- DECIMAL_SCALE = 5
37
- host = os.environ.get("RPC_HOST", default_host)
38
- base_url = f"http://{host}:3006/api"
39
- mempool = Mempool(base_url=base_url)
40
-
41
- def __init__(self, addresses_to_listen: {str}):
42
- self.addresses_to_listen = addresses_to_listen
43
-
44
- @abstractmethod
45
- def consume(self, subscribed_address, address_tx_data: [AddressTxData]):
46
- pass
47
-
48
- def filter_address_tx_data(self, address_tx_data: [AddressTxData]) -> [str]:
49
- filtered_address_tx_data = list(filter(lambda x: x.address in self.addresses_to_listen and x.address != "",
50
- address_tx_data))
51
- # get all address
52
- return list(set((map(lambda x: x.address, filtered_address_tx_data))))
53
-
54
- def on_tx(self, tx: Transaction):
55
- # get all address in the inputs and outputs along with the amount
56
- if tx.coinbase:
57
- return
58
- outputs = tx.outputs
59
- tx_id = tx.txid
60
- tx = self.mempool.get_transaction(tx_id)
61
- address_tx_data = []
62
- # getting inputs data separately from mempool
63
- # as current library doesn't provide rich data like previous outputs and its value
64
- for input in tx.vins:
65
- address = input.prev_out.address
66
- amount = input.prev_out.value
67
- address_tx_data.append(AddressTxData(address=address,
68
- type=AddressTxType.INPUT,
69
- _amount=amount,
70
- tx_id=tx_id))
71
- for output in outputs:
72
- amount = output.value
73
- address_tx_data.append(AddressTxData(address=output.address,
74
- _amount=amount,
75
- type=AddressTxType.OUTPUT,
76
- tx_id=tx_id))
77
-
78
- # filter the address we are interested in
79
- addresses_for_events = self.filter_address_tx_data(address_tx_data)
80
- for address in addresses_for_events:
81
- self.consume(subscribed_address=address, address_tx_data=address_tx_data)
File without changes
File without changes
File without changes