blockchain-parser-2 0.1.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.
Files changed (40) hide show
  1. blockchain_parser/__init__.py +12 -0
  2. blockchain_parser/address.py +83 -0
  3. blockchain_parser/block.py +104 -0
  4. blockchain_parser/block_header.py +90 -0
  5. blockchain_parser/blockchain.py +302 -0
  6. blockchain_parser/index.py +81 -0
  7. blockchain_parser/input.py +78 -0
  8. blockchain_parser/output.py +145 -0
  9. blockchain_parser/script.py +164 -0
  10. blockchain_parser/tests/__init__.py +10 -0
  11. blockchain_parser/tests/data/bech32_p2wpkh.txt +1 -0
  12. blockchain_parser/tests/data/bech32_p2wsh.txt +1 -0
  13. blockchain_parser/tests/data/bip69_false.txt +1 -0
  14. blockchain_parser/tests/data/bip69_true.txt +1 -0
  15. blockchain_parser/tests/data/genesis_block.txt +1 -0
  16. blockchain_parser/tests/data/invalid_tx.txt +1 -0
  17. blockchain_parser/tests/data/large_tx.txt +1 -0
  18. blockchain_parser/tests/data/scripts_invalid.txt +1 -0
  19. blockchain_parser/tests/data/segwit.txt +1 -0
  20. blockchain_parser/tests/data/size_non_segwit.txt +1 -0
  21. blockchain_parser/tests/data/size_segwit.txt +1 -0
  22. blockchain_parser/tests/test_address.py +45 -0
  23. blockchain_parser/tests/test_block.py +55 -0
  24. blockchain_parser/tests/test_blockchain.py +79 -0
  25. blockchain_parser/tests/test_index.py +48 -0
  26. blockchain_parser/tests/test_output.py +66 -0
  27. blockchain_parser/tests/test_script.py +171 -0
  28. blockchain_parser/tests/test_transaction.py +138 -0
  29. blockchain_parser/tests/test_undo.py +70 -0
  30. blockchain_parser/tests/test_utils.py +55 -0
  31. blockchain_parser/tests/utils.py +9 -0
  32. blockchain_parser/transaction.py +196 -0
  33. blockchain_parser/undo.py +198 -0
  34. blockchain_parser/utils.py +152 -0
  35. blockchain_parser/utils_taproot.py +143 -0
  36. blockchain_parser_2-0.1.6.dist-info/METADATA +143 -0
  37. blockchain_parser_2-0.1.6.dist-info/RECORD +40 -0
  38. blockchain_parser_2-0.1.6.dist-info/WHEEL +5 -0
  39. blockchain_parser_2-0.1.6.dist-info/licenses/LICENSE +177 -0
  40. blockchain_parser_2-0.1.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,78 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
11
+
12
+ from .utils import decode_compactsize, decode_uint32, format_hash
13
+ from .script import Script
14
+
15
+
16
+ class Input(object):
17
+ """Represents a transaction input"""
18
+
19
+ def __init__(self, raw_hex):
20
+ self._transaction_hash = None
21
+ self._transaction_index = None
22
+ self._script = None
23
+ self._sequence_number = None
24
+ self._witnesses = []
25
+
26
+ self._script_length, varint_length = decode_compactsize(raw_hex[36:])
27
+ self._script_start = 36 + varint_length
28
+
29
+ self.size = self._script_start + self._script_length + 4
30
+ self.hex = raw_hex[:self.size]
31
+
32
+ def add_witness(self, witness):
33
+ self._witnesses.append(witness)
34
+
35
+ @classmethod
36
+ def from_hex(cls, hex_):
37
+ return cls(hex_)
38
+
39
+ def __repr__(self):
40
+ return "Input(%s,%d)" % (self.transaction_hash, self.transaction_index)
41
+
42
+ @property
43
+ def transaction_hash(self):
44
+ """Returns the hash of the transaction containing the output
45
+ redeemed by this input"""
46
+ if self._transaction_hash is None:
47
+ self._transaction_hash = format_hash(self.hex[:32])
48
+ return self._transaction_hash
49
+
50
+ @property
51
+ def transaction_index(self):
52
+ """Returns the index of the output inside the transaction that is
53
+ redeemed by this input"""
54
+ if self._transaction_index is None:
55
+ self._transaction_index = decode_uint32(self.hex[32:36])
56
+ return self._transaction_index
57
+
58
+ @property
59
+ def sequence_number(self):
60
+ """Returns the input's sequence number"""
61
+ if self._sequence_number is None:
62
+ self._sequence_number = decode_uint32(
63
+ self.hex[self.size-4:self.size]
64
+ )
65
+ return self._sequence_number
66
+
67
+ @property
68
+ def script(self):
69
+ """Returns a Script object representing the redeem script"""
70
+ if self._script is None:
71
+ end = self._script_start + self._script_length
72
+ self._script = Script.from_hex(self.hex[self._script_start:end])
73
+ return self._script
74
+
75
+ @property
76
+ def witnesses(self):
77
+ """Return a list of witness data attached to this input, empty if non segwit"""
78
+ return self._witnesses
@@ -0,0 +1,145 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
11
+
12
+ from .utils import decode_compactsize, decode_uint64
13
+ from .script import Script
14
+ from .address import Address
15
+
16
+
17
+ class Output(object):
18
+ """Represents a Transaction output"""
19
+
20
+ def __init__(self, raw_hex):
21
+ self._value = None
22
+ self._script = None
23
+ self._addresses = None
24
+
25
+ script_length, varint_size = decode_compactsize(raw_hex[8:])
26
+ script_start = 8 + varint_size
27
+
28
+ self._script_hex = raw_hex[script_start:script_start+script_length]
29
+ self.size = script_start + script_length
30
+ self._value_hex = raw_hex[:8]
31
+
32
+ @classmethod
33
+ def from_hex(cls, hex_):
34
+ return cls(hex_)
35
+
36
+ def __repr__(self):
37
+ return "Output(satoshis=%d)" % self.value
38
+
39
+ @property
40
+ def value(self):
41
+ """Returns the value of the output expressed in satoshis"""
42
+ if self._value is None:
43
+ self._value = decode_uint64(self._value_hex)
44
+ return self._value
45
+
46
+ @property
47
+ def script(self):
48
+ """Returns the output's script as a Script object"""
49
+ if self._script is None:
50
+ self._script = Script.from_hex(self._script_hex)
51
+ return self._script
52
+
53
+ @property
54
+ def addresses(self):
55
+ """Returns a list containing all the addresses mentioned
56
+ in the output's script
57
+ """
58
+ if self._addresses is None:
59
+ self._addresses = []
60
+ if self.type == "pubkey":
61
+ address = Address.from_public_key(self.script.operations[0])
62
+ self._addresses.append(address)
63
+ elif self.type == "pubkeyhash":
64
+ address = Address.from_ripemd160(self.script.operations[2])
65
+ self._addresses.append(address)
66
+ elif self.type == "p2sh":
67
+ address = Address.from_ripemd160(self.script.operations[1],
68
+ type="p2sh")
69
+ self._addresses.append(address)
70
+ elif self.type == "multisig":
71
+ for operation in self.script.operations[1:-2]:
72
+ if isinstance(operation, (bytes, bytearray)):
73
+ self._addresses.append(
74
+ Address.from_public_key(operation)
75
+ )
76
+ elif self.type == "p2wpkh":
77
+ address = Address.from_bech32(self.script.operations[1], 0)
78
+ self._addresses.append(address)
79
+ elif self.type == "p2wsh":
80
+ address = Address.from_bech32(self.script.operations[1], 0)
81
+ self._addresses.append(address)
82
+ elif self.type == "p2tr":
83
+ address = Address.from_bech32m(self.script.operations[1], 1)
84
+ self._addresses.append(address)
85
+ return self._addresses
86
+
87
+ def is_return(self):
88
+ return self.script.is_return()
89
+
90
+ def is_p2sh(self):
91
+ return self.script.is_p2sh()
92
+
93
+ def is_pubkey(self):
94
+ return self.script.is_pubkey()
95
+
96
+ def is_pubkeyhash(self):
97
+ return self.script.is_pubkeyhash()
98
+
99
+ def is_multisig(self):
100
+ return self.script.is_multisig()
101
+
102
+ def is_unknown(self):
103
+ return self.script.is_unknown()
104
+
105
+ def is_p2wpkh(self):
106
+ return self.script.is_p2wpkh()
107
+
108
+ def is_p2wsh(self):
109
+ return self.script.is_p2wsh()
110
+
111
+ def is_p2tr(self):
112
+ return self.script.is_p2tr()
113
+
114
+ @property
115
+ def type(self):
116
+ """Returns the output's script type as a string"""
117
+ # Fix for issue 11
118
+ if not self.script.script.is_valid():
119
+ return "invalid"
120
+
121
+ if self.is_pubkeyhash():
122
+ return "pubkeyhash"
123
+
124
+ if self.is_pubkey():
125
+ return "pubkey"
126
+
127
+ if self.is_p2sh():
128
+ return "p2sh"
129
+
130
+ if self.is_multisig():
131
+ return "multisig"
132
+
133
+ if self.is_return():
134
+ return "OP_RETURN"
135
+
136
+ if self.is_p2wpkh():
137
+ return "p2wpkh"
138
+
139
+ if self.is_p2wsh():
140
+ return "p2wsh"
141
+
142
+ if self.is_p2tr():
143
+ return "p2tr"
144
+
145
+ return "unknown"
@@ -0,0 +1,164 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
11
+
12
+ from bitcoin.core.script import *
13
+ from binascii import b2a_hex
14
+
15
+
16
+ def is_public_key(hex_data):
17
+ """Given a bytes string, returns whether its is probably a bitcoin
18
+ public key or not. It doesn't actually ensures that the data is a valid
19
+ public key, justs that it looks like one
20
+ """
21
+ if type(hex_data) != bytes:
22
+ return False
23
+
24
+ # Uncompressed public key
25
+ if len(hex_data) == 65 and int(hex_data[0]) == 4:
26
+ return True
27
+
28
+ # Compressed public key
29
+ if len(hex_data) == 33 and int(hex_data[0]) in [2, 3]:
30
+ return True
31
+
32
+ return False
33
+
34
+
35
+ class Script(object):
36
+ """Represents a bitcoin script contained in an input or output"""
37
+
38
+ def __init__(self, raw_hex):
39
+ self.hex = raw_hex
40
+ self._script = None
41
+ self._type = None
42
+ self._value = None
43
+ self._operations = None
44
+ self._addresses = None
45
+
46
+ @classmethod
47
+ def from_hex(cls, hex_):
48
+ return cls(hex_)
49
+
50
+ def __repr__(self):
51
+ return "Script(%s)" % self.value
52
+
53
+ @property
54
+ def script(self):
55
+ """Returns the underlying CScript object"""
56
+ if self._script is None:
57
+ self._script = CScript(self.hex)
58
+
59
+ return self._script
60
+
61
+ @property
62
+ def operations(self):
63
+ """Returns the list of operations done by this script,
64
+ an operation is one of :
65
+ - a CScriptOP
66
+ - bytes data pushed to the stack
67
+ - an int pushed to the stack
68
+ If the script is invalid (some coinbase scripts are), an exception is
69
+ thrown
70
+ """
71
+ if self._operations is None:
72
+ # Some coinbase scripts are garbage, they could not be valid
73
+ try:
74
+ self._operations = list(self.script)
75
+ except CScriptInvalidError:
76
+ self._operations = []
77
+
78
+ return self._operations
79
+
80
+ @property
81
+ def value(self):
82
+ """Returns a string representation of the script"""
83
+ if self._value is None:
84
+ parts = []
85
+ try:
86
+ for operation in list(self.script):
87
+ if isinstance(operation, bytes):
88
+ parts.append(b2a_hex(operation).decode("ascii"))
89
+ else:
90
+ parts.append(str(operation))
91
+
92
+ self._value = " ".join(parts)
93
+ except CScriptInvalidError:
94
+ self._value = "INVALID_SCRIPT"
95
+
96
+ return self._value
97
+
98
+ def is_return(self):
99
+ return self.script.is_unspendable()
100
+
101
+ def is_p2sh(self):
102
+ return self.script.is_p2sh()
103
+
104
+ def is_p2wsh(self):
105
+ return self.script.is_witness_v0_scripthash()
106
+
107
+ def is_p2wpkh(self):
108
+ return self.script.is_witness_v0_keyhash()
109
+
110
+ def is_p2tr(self):
111
+ """Return whether this is a BIP341 taproot output (OP_1 <32 bytes>)."""
112
+ operations = self.operations
113
+ return (
114
+ len(self.hex) == 34
115
+ and len(operations) == 2
116
+ and operations[0] == 1
117
+ and isinstance(operations[1], (bytes, bytearray))
118
+ and len(operations[1]) == 32
119
+ )
120
+
121
+ def is_pubkey(self):
122
+ return len(self.operations) == 2 \
123
+ and self.operations[-1] == OP_CHECKSIG \
124
+ and is_public_key(self.operations[0])
125
+
126
+ def is_pubkeyhash(self):
127
+ operations = self.operations
128
+ if len(self.hex) != 25 or len(operations) != 5:
129
+ return False
130
+ pushed = operations[2]
131
+ return (
132
+ operations[0] == OP_DUP
133
+ and operations[1] == OP_HASH160
134
+ and isinstance(pushed, (bytes, bytearray))
135
+ and len(pushed) == 20
136
+ and operations[-2] == OP_EQUALVERIFY
137
+ and operations[-1] == OP_CHECKSIG
138
+ )
139
+
140
+ def is_multisig(self):
141
+ operations = self.operations
142
+ if len(operations) < 4:
143
+ return False
144
+ m = operations[0]
145
+ n = operations[-2]
146
+ if not isinstance(m, int) or not isinstance(n, int):
147
+ return False
148
+ if m < 0 or n < 1 or m > n or n > 16:
149
+ return False
150
+ if operations[-1] != OP_CHECKMULTISIG:
151
+ return False
152
+ # OP_m, exactly n pubkeys, OP_n, OP_CHECKMULTISIG
153
+ if len(operations) != n + 3:
154
+ return False
155
+ for operation in operations[1:-2]:
156
+ if not is_public_key(operation):
157
+ return False
158
+ return True
159
+
160
+ def is_unknown(self):
161
+ return not self.is_pubkeyhash() and not self.is_pubkey() \
162
+ and not self.is_p2sh() and not self.is_multisig() \
163
+ and not self.is_return() and not self.is_p2wpkh() \
164
+ and not self.is_p2wsh() and not self.is_p2tr()
@@ -0,0 +1,10 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
@@ -0,0 +1 @@
1
+ 020000000001016dee1ae581bf13134aa6aea8051b974c064bc2227094dc971561aac44a02ec500000000000ffffffff023cad17000000000017a914682ca28dc5e71f0139ad95a71c639c9e61c9c82e8773a33a0400000000160014a89e7f54db49b31364169f3e8982fcb5d3275e8b024830450221009424d2dcee3e8a00261cd3df8c7a103b14a8f0853075118329a21856f9cef36802207b9a2d51347bd2f73359375260e29fb67c67fa87825a625362a16b4a3c38f2350121030731907d43214f178bd4c9e279ac22043858001e220a935fff94f5fd3e8606a300000000
@@ -0,0 +1 @@
1
+ 020000000001019425b8169d86af46b96afb6d8e1f40af44fedbb27fab196a88d1ae95b23230fd0200000000000000000260c084000000000017a914a0d015cec889e4cce8b359c6d607346e3985d66587fca67410000000002200206f49e7412b232a3d02c199caff40e9246d935a64799c369db577ecfd0929113804004730440220706ee05f17d6dee8ad42f3bf714a7e4911fed31325bad04284bc4ed7f3261e6b02203d72b089ee893164b203db722517a5bde37c791a3a256976c0379c73ad9f30110147304402207e36fcea59efb1b371dcabbe6766fb6334a498fe4769b263c00c7774df74b7f502200aea2ec5bab47ac16ed67cfc709665cfdcbd4f48d187a6a043267fe96789e1ce0169522102820ade490833f7c3e1abf944caed54b43f88f255f781a393ac4a6eedf04ff4862103c31b3287a9d6820a67c00af26c4955957ddf5fd21312c0ac8f11021b3f6192af2103ae579b44326ac0ba725038a982e3873f338c107b9d169172ca49e5bc7178469153ae00000000
@@ -0,0 +1 @@
1
+ 0100000011aad553bb1650007e9982a8ac79d227cd8c831e1573b11f25573a37664e5f3e64000000006a47304402205438cedd30ee828b0938a863e08d810526123746c1f4abee5b7bc2312373450c02207f26914f4275f8f0040ab3375bacc8c5d610c095db8ed0785de5dc57456591a601210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffc26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f5485d1fde028000000006b483045022100f81d98c1de9bb61063a5e6671d191b400fda3a07d886e663799760393405439d0220234303c9af4bad3d665f00277fe70cdd26cd56679f114a40d9107249d29c979401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff456a9e597129f5df2e11b842833fc19a94c563f57449281d3cd01249a830a1f0000000006a47304402202310b00924794ef68a8f09564fd0bb128838c66bc45d1a3f95c5cab52680f166022039fc99138c29f6c434012b14aca651b1c02d97324d6bd9dd0ffced0782c7e3bd01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff571fb3e02278217852dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e000000006b483045022100d276251f1f4479d8521269ec8b1b45c6f0e779fcf1658ec627689fa8a55a9ca50220212a1e307e6182479818c543e1b47d62e4fc3ce6cc7fc78183c7071d245839df01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff5d8de50362ff33d3526ac3602e9ee25c1a349def086a7fc1d9941aaeb9e91d38010000006b4830450221008768eeb1240451c127b88d89047dd387d13357ce5496726fc7813edc6acd55ac022015187451c3fb66629af38fdb061dfb39899244b15c45e4a7ccc31064a059730d01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff60ad3408b89ea19caf3abd5e74e7a084344987c64b1563af52242e9d2a8320f3000000006b4830450221009be4261ec050ebf33fa3d47248c7086e4c247cafbb100ea7cee4aa81cd1383f5022008a70d6402b153560096c849d7da6fe61c771a60e41ff457aac30673ceceafee01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe9b483a8ac4129780c88d1babe41e89dc10a26dedbf14f80a28474e9a11104de010000006b4830450221009bc40eee321b39b5dc26883f79cd1f5a226fc6eed9e79e21d828f4c23190c57e022078182fd6086e265589105023d9efa4cba83f38c674a499481bd54eee196b033f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffe28db9462d3004e21e765e03a45ecb147f136a20ba8bca78ba60ebfc8e2f8b3b000000006a47304402200fb572b7c6916515452e370c2b6f97fcae54abe0793d804a5a53e419983fae1602205191984b6928bf4a1e25b00e5b5569a0ce1ecb82db2dea75fe4378673b53b9e801210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff7a1ef65ff1b7b7740c662ab6c9735ace4a16279c23a1db5709ed652918ffff54010000006a47304402206bc218a925f7280d615c8ea4f0131a9f26e7fc64cff6eeeb44edb88aba14f1910220779d5d67231bc2d2d93c3c5ab74dcd193dd3d04023e58709ad7ffbf95161be6201210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff850cecf958468ca7ffa6a490afe13b8c271b1326b0ddc1fdfdf9f3c7e365fdba000000006a473044022047df98cc26bd2bfdc5b2b97c27aead78a214810ff023e721339292d5ce50823d02205fe99dc5f667908974dae40cc7a9475af7fa6671ba44f64a00fcd01fa12ab523012102ca46fa75454650afba1784bc7b079d687e808634411e4beff1f70e44596308a1ffffffff8640e312040e476cf6727c60ca3f4a3ad51623500aacdda96e7728dbdd99e8a5000000006a47304402205566aa84d3d84226d5ab93e6f253b57b3ef37eb09bb73441dae35de86271352a02206ee0b7f800f73695a2073a2967c9ad99e19f6ddf18ce877adf822e408ba9291e01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff91c1889c5c24b93b56e643121f7a05a34c10c5495c450504c7b5afcb37e11d7a000000006b483045022100df61d45bbaa4571cdd6c5c822cba458cdc55285cdf7ba9cd5bb9fc18096deb9102201caf8c771204df7fd7c920c4489da7bc3a60e1d23c1a97e237c63afe53250b4a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff2470947216eb81ea0eeeb4fe19362ec05767db01c3aa3006bb499e8b6d6eaa26010000006a473044022031501a0b2846b8822a32b9947b058d89d32fc758e009fc2130c2e5effc925af70220574ef3c9e350cef726c75114f0701fd8b188c6ec5f84adce0ed5c393828a5ae001210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff0abcd77d65cc14363f8262898335f184d6da5ad060ff9e40bf201741022c2b40010000006b483045022100a6ac110802b699f9a2bff0eea252d32e3d572b19214d49d8bb7405efa2af28f1022033b7563eb595f6d7ed7ec01734e17b505214fe0851352ed9c3c8120d53268e9a01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffa43bebbebf07452a893a95bfea1d5db338d23579be172fe803dce02eeb7c037d010000006b483045022100ebc77ed0f11d15fe630fe533dc350c2ddc1c81cfeb81d5a27d0587163f58a28c02200983b2a32a1014bab633bfc9258083ac282b79566b6b3fa45c1e6758610444f401210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb102113fa46ce949616d9cda00f6b10231336b3928eaaac6bfe42d1bf3561d6c010000006a473044022010f8731929a55c1c49610722e965635529ed895b2292d781b183d465799906b20220098359adcbc669cd4b294cc129b110fe035d2f76517248f4b7129f3bf793d07f01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffffb861fab2cde188499758346be46b5fbec635addfc4e7b0c8a07c0a908f2b11b4000000006a47304402207328142bb02ef5d6496a210300f4aea71f67683b842fa3df32cae6c88b49a9bb022020f56ddff5042260cfda2c9f39b7dec858cc2f4a76a987cd2dc25945b04e15fe01210391064d5b2d1c70f264969046fcff853a7e2bfde5d121d38dc5ebd7bc37c2b210ffffffff027064d817000000001976a9144a5fba237213a062f6f57978f796390bdcf8d01588ac00902f50090000001976a9145be32612930b8323add2212a4ec03c1562084f8488ac00000000
@@ -0,0 +1 @@
1
+ 010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00000000
@@ -0,0 +1 @@
1
+ 0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000
@@ -0,0 +1 @@
1
+ 010000000b04a4abee8360f7a660bcf1c298496d927b6ad1f25dd12485e6a572e275cbd43f000000008b483045022100c6d59290df934bac08bc4124b06154ee21394d37ec958d145129a885dbd63eab0220036251120e4ceacc8e9ff13585049945c7dbdb85921a6e077a80c7e410931f4a014104f3ce8948c592690cb36a7e6e31534927569f41e45c77fbdb95fdb0e235717a295b2a281c04619139c453dd9139e951086325b4021bd28da6fc3da86943cd04cfffffffff21fd91e6dd64838eadce9bc0d687af571779c3af4b53bada845485466decf005000000008b48304502205d309b632a816f1f2479b8ca284301fce3fc8f979306722bba4cf28c8fa7ed76022100a3313ee59bbf0e61e29302b2ea597a993a73e93be841f2f7dd3f6e9623572793014104ca3b25750307a483c0cff8efb1413c98b4e18765485d94eac147f21f507169997a72aeb50f0b01ed6b468c9554143125f63c0b2b229a597e695718b86d711607ffffffff4dd6c46e3e68fc05243b6f894e2f3e4d2782d1c8a3d17b9a2c5e78912bbaf433010000008b483045022100f3f1d6c0e76a00e89a5278fa14eae8de93454174199f72c98aef54dabb2074d10220172dc0d6958efa8dc18c621f2d56fd0484ecb04e3d65e2284268916c1c324ade014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffff52aabc7fdee0588d63fff3fc00e20b00a87cffa6a37aad7168741b1f0e77bdff000000008b4830450220274a5a9577f0786fb17d373b6d1995a6cff752320bf4e3d5ee41b15fdd86ef2a022100fd1d0c0f43d05e479430c3c9bd5481f5be929a7f2b82d14292ef192653c3b94c014104988828ea7144ff00def9988e88aad98b1f22d01000fc4b52d1003c9b32de0534cff71c0a045098bb28f699bcc9db13533c0998eb2133085d37cac65b5bd74c1fffffffff5aab5581c575a56a803ef5d8698dc34014261d196ada1da3300731380d7e672d000000008b48304502201615a116a2e057f3f8f2a63340d0387b820a6f9d1067758f4f6cdec76c04951a022100a1ada9e03a0d9603743d41347ffd992642544d6dfdc51121b7bf622e12cb1547014104830b3b139b0a8707f348840b0fa358164869fbc0c423a2785cb0a18ff73517f8878cefd643e86e682eb93d07216f76993c9e980592de07c507c3a2659efff6aaffffffff74d7cd370a334aabee3b8328b67a63f1768583405e73ebd291dd86189983a61c000000008a4730440220432ff0f90a3f5372a8dc4b3d2e7165e110efa16b40784ea4d5ab866b736712ed02201a759b630d562f4dbc4a7d2fca406ad54e235564346bc360dec26d6e72bb60fa014104ca0bb72303672885ad953279309694c71574d50d2c6e1f5f1e7ab1b0bf3df67e5439b3f6e2501dda539812e414991ebaa547631374ce2bda9dd7211099886827ffffffff8941eb7fe688ff48357981c4756906e331c07f332429be5b2cfc7a945644f80f000000008a47304402204e074678b525927a8f459d1dc93ad738ce02c0ea77c50a749647032eda662eee02203c1a98e9b116dc2fe9e7325a5696ad8e3ad26d2d10503e6e3209f4f5f40f6e34014104f9baa86b46b04f2a2dc4270903bcca9444026b7f184d879f089462e332f260f6de8e05f08285a4a3c716220d9ec0f7e9fde354c779f32f00dd4d4cd8d4a1ac28ffffffff948be2e03f9537a35319e8cee2b7b9ed2769ca59734e5d4b42951ef7ba3bb4a8000000008b483045022100fcf1deaaa047e8f1b8b8ac783760ea5a0af15adbb9eed1c1748942a57510e27402204c278b12c4991480dc714af66395f5d8f42e411423bf27b024f62267c45a0d37014104cfd128c3f322b59c21d452d5c4c7b4d1a3237a2d31c6f4033b32db78c19cb342ffc151acc272a02a13ba985a523b655689d8118f502b8a7fb8df5fc7bd8458ceffffffffb2cae04cf238b4d47c614da5ce9948f089a02f1ff3e22cda6b9f9196bda87215010000008b483045022017a74373ef19756e275f2d987925a8bd22cfaf1c93eede7db63102dc73b7cfd0022100fd2f040d7d3ccd1a2188923f8c7560253aa2a910d8e9cd06a8a05fc69c12f6ce014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffffbe75c9f1faf79fb885018e3353ada4a7fb5c989bc26f031c5aee69d1eabad957000000008a4730440220360d35b0a98dd086d242f8976aa704b808541ad78ef5cba45e4eb73c1efbaaa7022025a3d981b9e0836a2493698b802bf7f0eae72de0252c7eac059ad14b1b27001b014104d969f0d456196f4c16945bc71fc460615002d1554fc8d774288aa8fc5c9eca1622a3dcb1e6a8314c94844e516c0543316b2e75d465c8fc2f0d0d20334e53f7dbffffffffbcbad95cac8bbe08538f7b12c240e96866358dfa819de6e940bb60ce7fae5c3d000000008b483045022100d916990888d0d26246b8d6cb944b1e8ebd6a4cdb2965ff7e4430883eef02c4e102205693b96aa1d6e9335f3cb2986b7f3a03357959e989570d03a56ccdb18a8d7e6d0141040840a410ed7afdc3e3fe3923ae388413bbd8a9089062403e0adce62212bccfe1f80400530121cfdba5b52f3e16397f1959b14a09089778afd5ff41f8a856980fffffffff02005039278c0400001976a914f1c87a5e8ff7d14e74b858089bf771c94b1b6db488ac00203d88792d00001976a914dcdf2f9892bfa1cb086530354eab3ba078a2f0
@@ -0,0 +1 @@
1
+ 010000000b04a4abee8360f7a660bcf1c298496d927b6ad1f25dd12485e6a572e275cbd43f000000008b483045022100c6d59290df934bac08bc4124b06154ee21394d37ec958d145129a885dbd63eab0220036251120e4ceacc8e9ff13585049945c7dbdb85921a6e077a80c7e410931f4a014104f3ce8948c592690cb36a7e6e31534927569f41e45c77fbdb95fdb0e235717a295b2a281c04619139c453dd9139e951086325b4021bd28da6fc3da86943cd04cfffffffff21fd91e6dd64838eadce9bc0d687af571779c3af4b53bada845485466decf005000000008b48304502205d309b632a816f1f2479b8ca284301fce3fc8f979306722bba4cf28c8fa7ed76022100a3313ee59bbf0e61e29302b2ea597a993a73e93be841f2f7dd3f6e9623572793014104ca3b25750307a483c0cff8efb1413c98b4e18765485d94eac147f21f507169997a72aeb50f0b01ed6b468c9554143125f63c0b2b229a597e695718b86d711607ffffffff4dd6c46e3e68fc05243b6f894e2f3e4d2782d1c8a3d17b9a2c5e78912bbaf433010000008b483045022100f3f1d6c0e76a00e89a5278fa14eae8de93454174199f72c98aef54dabb2074d10220172dc0d6958efa8dc18c621f2d56fd0484ecb04e3d65e2284268916c1c324ade014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffff52aabc7fdee0588d63fff3fc00e20b00a87cffa6a37aad7168741b1f0e77bdff000000008b4830450220274a5a9577f0786fb17d373b6d1995a6cff752320bf4e3d5ee41b15fdd86ef2a022100fd1d0c0f43d05e479430c3c9bd5481f5be929a7f2b82d14292ef192653c3b94c014104988828ea7144ff00def9988e88aad98b1f22d01000fc4b52d1003c9b32de0534cff71c0a045098bb28f699bcc9db13533c0998eb2133085d37cac65b5bd74c1fffffffff5aab5581c575a56a803ef5d8698dc34014261d196ada1da3300731380d7e672d000000008b48304502201615a116a2e057f3f8f2a63340d0387b820a6f9d1067758f4f6cdec76c04951a022100a1ada9e03a0d9603743d41347ffd992642544d6dfdc51121b7bf622e12cb1547014104830b3b139b0a8707f348840b0fa358164869fbc0c423a2785cb0a18ff73517f8878cefd643e86e682eb93d07216f76993c9e980592de07c507c3a2659efff6aaffffffff74d7cd370a334aabee3b8328b67a63f1768583405e73ebd291dd86189983a61c000000008a4730440220432ff0f90a3f5372a8dc4b3d2e7165e110efa16b40784ea4d5ab866b736712ed02201a759b630d562f4dbc4a7d2fca406ad54e235564346bc360dec26d6e72bb60fa014104ca0bb72303672885ad953279309694c71574d50d2c6e1f5f1e7ab1b0bf3df67e5439b3f6e2501dda539812e414991ebaa547631374ce2bda9dd7211099886827ffffffff8941eb7fe688ff48357981c4756906e331c07f332429be5b2cfc7a945644f80f000000008a47304402204e074678b525927a8f459d1dc93ad738ce02c0ea77c50a749647032eda662eee02203c1a98e9b116dc2fe9e7325a5696ad8e3ad26d2d10503e6e3209f4f5f40f6e34014104f9baa86b46b04f2a2dc4270903bcca9444026b7f184d879f089462e332f260f6de8e05f08285a4a3c716220d9ec0f7e9fde354c779f32f00dd4d4cd8d4a1ac28ffffffff948be2e03f9537a35319e8cee2b7b9ed2769ca59734e5d4b42951ef7ba3bb4a8000000008b483045022100fcf1deaaa047e8f1b8b8ac783760ea5a0af15adbb9eed1c1748942a57510e27402204c278b12c4991480dc714af66395f5d8f42e411423bf27b024f62267c45a0d37014104cfd128c3f322b59c21d452d5c4c7b4d1a3237a2d31c6f4033b32db78c19cb342ffc151acc272a02a13ba985a523b655689d8118f502b8a7fb8df5fc7bd8458ceffffffffb2cae04cf238b4d47c614da5ce9948f089a02f1ff3e22cda6b9f9196bda87215010000008b483045022017a74373ef19756e275f2d987925a8bd22cfaf1c93eede7db63102dc73b7cfd0022100fd2f040d7d3ccd1a2188923f8c7560253aa2a910d8e9cd06a8a05fc69c12f6ce014104daeab6af397baf06fcc91b6c04a28ffb2cd80312e954a2a55f7c864094608032ef657e8296dfc2f86ecf28fe32925c0f648d3ef16b96934a34b29dd871ff057affffffffbe75c9f1faf79fb885018e3353ada4a7fb5c989bc26f031c5aee69d1eabad957000000008a4730440220360d35b0a98dd086d242f8976aa704b808541ad78ef5cba45e4eb73c1efbaaa7022025a3d981b9e0836a2493698b802bf7f0eae72de0252c7eac059ad14b1b27001b014104d969f0d456196f4c16945bc71fc460615002d1554fc8d774288aa8fc5c9eca1622a3dcb1e6a8314c94844e516c0543316b2e75d465c8fc2f0d0d20334e53f7dbffffffffbcbad95cac8bbe08538f7b12c240e96866358dfa819de6e940bb60ce7fae5c3d000000008b483045022100d916990888d0d26246b8d6cb944b1e8ebd6a4cdb2965ff7e4430883eef02c4e102205693b96aa1d6e9335f3cb2986b7f3a03357959e989570d03a56ccdb18a8d7e6d0141040840a410ed7afdc3e3fe3923ae388413bbd8a9089062403e0adce62212bccfe1f80400530121cfdba5b52f3e16397f1959b14a09089778afd5ff41f8a856980fffffffff02005039278c0400001976a914f1c87a5e8ff7d14e74b858089bf771c94b1b6db488ac00203d88792d00001976a914dcdf2f9892bfa1cb086530354eab3ba078a2f09088ac00000000
@@ -0,0 +1 @@
1
+ 01000000019ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d010000006b4830450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa012102d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fdffffffff080100000000000000010101000000000000000202010100000000000000014c0100000000000000034c02010100000000000000014d0100000000000000044dffff010100000000000000014e0100000000000000064effffffff0100000000
@@ -0,0 +1 @@
1
+ 010000000001019750f68b12fc617dc777d1420e1a34700d763f105303d7199348ff4b315da6b11300000000ffffffff0440164000000000001976a914ad62f43b9eba175ae6df7b3482a4fe6a3dad755588ac00c2eb0b000000001976a91415efba4572d98d9d19be7378e85fe6b2f5a111fb88ac80584f00000000001976a9147cce8012dd98ffa09129016d2fdedf8725b8c0ef88ac60a3a90c00000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d0400483045022100bc2ba8808127f8a74beed6dfa1b9fe54675c55aab85a61d7a74c15b993e67e5f02204dada4e15f0b4e659dae7bf0d0f648010d1f2b665f587a35eb6f22e44194952301483045022100f4c7ec7c2064fe2cc4389733ac0a57d8080a62180a004b02a19b89267113a17f022004ee9fdb081359c549ee42ffb58279363563eaf0191cd8b2b0ceebf62146b50b01695221022b003d276bce58bef509bdcd9cf7e156f0eae18e1175815282e65e7da788bb5b21035c58f2f60ecf38c9c8b9d1316b662627ec672f5fd912b1a2cc28d0b9b00575fd2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000
@@ -0,0 +1 @@
1
+ 010000000153dc411869f3b96f3dc48b6ac52438e895d6641142e0d4f6383fabc20e49e761010000006a4730440220177a4f6729b41ee6a06c2e4826b12313d9703e6fd3ee6cca347b4418cfe3a17b0220177ed399130c11f1b37a97f6e7982fc654f70139de0361eddc5d1cbf88d0c6e001210258764172106289665db078db8c6c3510e17e93caa0522d3ffca30a75562699b1ffffffff01ca06fc000000000017a914169e3bb06b5f0355e5085a8a1a5e430a3f6b39258700000000
@@ -0,0 +1 @@
1
+ 01000000000101621adcd8ea06cc206490ad64f938e30a5fffa01d0d529a29140a858414cb9f7f0100000023220020be38f99e2cb9f29f7a6c34c4e83f35dcc8dae66becd64dcef6d5e5f147301fb7ffffffff02dc2a0400000000001976a9140d599e7368f1f44f5ede79adafa58079e789443f88ac0f9b18000000000017a914cc0a76a97b96703b66b3f0c8adf619e9c3e22fac87040047304402203fbb18de9b0770882f62e2f2d2cb0b6e0256bbee0f95df5083a09f74dbab9f57022053919922e9752c11320d9b76134c659f1588095d42aecda340da5e103399ecaa01483045022100885fc1e985fac55aa3c018d0fb328fc90a2d23bc673bc22155aa62d8fa8963ad02202875de558913de4fc72386a653f1c60ab2ef42be572955675b46467f5c7906b901475221020c13434b1a50bf18863dde6ef0303c7dd4942459512679595b97ecd9b199e0f72102f78ef4eba789c98b5c92e55d139232dbacfa0d5b2dbbd3e727d4b3774dcd79fe52ae00000000
@@ -0,0 +1,45 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
11
+
12
+ import unittest
13
+ from binascii import a2b_hex
14
+
15
+ from blockchain_parser.address import Address
16
+
17
+
18
+ class TestUtils(unittest.TestCase):
19
+ def test_from_public_key(self):
20
+ public_key = "0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A" \
21
+ "04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38" \
22
+ "855ED6F2EE187E9C582BA6"
23
+ hex_pk = a2b_hex(public_key)
24
+ address = Address.from_public_key(hex_pk)
25
+ self.assertEqual(address.address, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")
26
+ address_hash = "010966776006953D5567439E5E39F86A0D273BEE"
27
+ self.assertEqual(address.hash, a2b_hex(address_hash))
28
+
29
+ def test_from_ripemd160(self):
30
+ ripemd160 = "010966776006953D5567439E5E39F86A0D273BEE"
31
+ address = Address.from_ripemd160(a2b_hex(ripemd160))
32
+ self.assertEqual(address.address, "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM")
33
+
34
+ def test_from_bech32(self):
35
+ # Example sourced from https://en.bitcoin.it/wiki/Bech32
36
+ bech32 = "751e76e8199196d454941c45d1b3a323f1433bd6"
37
+ address = Address.from_bech32(a2b_hex(bech32), segwit_version=0)
38
+ self.assertEqual(address.address, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
39
+
40
+ def test_from_bech32m(self):
41
+ # https://blockstream.info/tx/33e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac98799036?expand
42
+ # Second output
43
+ bech32m = "a37c3903c8d0db6512e2b40b0dffa05e5a3ab73603ce8c9c4b7771e5412328f9"
44
+ address = Address.from_bech32m(a2b_hex(bech32m), segwit_version=1)
45
+ self.assertEqual(address.address, "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297")
@@ -0,0 +1,55 @@
1
+ # Copyright (C) 2015-2016 The bitcoin-blockchain-parser developers
2
+ #
3
+ # This file is part of bitcoin-blockchain-parser.
4
+ #
5
+ # It is subject to the license terms in the LICENSE file found in the top-level
6
+ # directory of this distribution.
7
+ #
8
+ # No part of bitcoin-blockchain-parser, including this file, may be copied,
9
+ # modified, propagated, or distributed except according to the terms contained
10
+ # in the LICENSE file.
11
+
12
+ import unittest
13
+ from datetime import datetime, timezone
14
+
15
+ from .utils import read_test_data
16
+ from blockchain_parser.block import Block, get_block_transactions
17
+
18
+
19
+ class TestBlock(unittest.TestCase):
20
+ def test_from_hex(self):
21
+ block_hex = read_test_data("genesis_block.txt")
22
+ block = Block.from_hex(block_hex)
23
+ self.assertEqual(1, block.n_transactions)
24
+ block_hash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1" \
25
+ "b60a8ce26f"
26
+ self.assertEqual(block_hash, block.hash)
27
+ self.assertEqual(486604799, block.header.bits)
28
+ merkle_root = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127" \
29
+ "b7afdeda33b"
30
+ self.assertEqual(merkle_root, block.header.merkle_root)
31
+ self.assertEqual(2083236893, block.header.nonce)
32
+ self.assertEqual(1, block.header.version)
33
+ self.assertEqual(1, block.header.difficulty)
34
+ self.assertEqual(285, block.size)
35
+ self.assertEqual(datetime.fromtimestamp(1231006505, timezone.utc),
36
+ block.header.timestamp)
37
+ self.assertEqual("0" * 64, block.header.previous_block_hash)
38
+
39
+ for tx in block.transactions:
40
+ self.assertEqual(1, tx.version)
41
+ tx_hash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127" \
42
+ "b7afdeda33b"
43
+ self.assertEqual(tx_hash, tx.hash)
44
+ self.assertEqual(204, tx.size)
45
+ self.assertEqual(0, tx.locktime)
46
+ self.assertEqual(0xffffffff, tx.inputs[0].transaction_index)
47
+ self.assertEqual(0xffffffff, tx.inputs[0].sequence_number)
48
+ self.assertTrue("ffff001d" in tx.inputs[0].script.value)
49
+ self.assertEqual("0" * 64, tx.inputs[0].transaction_hash)
50
+ self.assertEqual(50 * 100000000, tx.outputs[0].value)
51
+
52
+ def test_truncated_transaction_raises(self):
53
+ raw_block = b"\x00" * 80 + b"\x01" + b"\xff" * 8
54
+ with self.assertRaises(ValueError):
55
+ list(get_block_transactions(raw_block))
@@ -0,0 +1,79 @@
1
+ import unittest
2
+
3
+ from blockchain_parser.blockchain import (
4
+ Blockchain,
5
+ iter_chain_indexes,
6
+ select_main_chain,
7
+ )
8
+
9
+
10
+ EASY = 0x1d00ffff
11
+ HARD = 0x1c00ffff
12
+ ZERO = "0" * 64
13
+
14
+
15
+ class Index(object):
16
+ def __init__(self, hash, height, prev, bits, file=0, data_pos=1):
17
+ self.hash = hash
18
+ self.height = height
19
+ self.prev_hash = prev
20
+ self.bits = bits
21
+ self.file = file
22
+ self.data_pos = data_pos
23
+
24
+
25
+ class TestMainChain(unittest.TestCase):
26
+ def test_longer_chain_beats_orphan(self):
27
+ genesis = Index("aa", 0, ZERO, EASY)
28
+ main = Index("bb", 1, "aa", EASY)
29
+ tip = Index("cc", 2, "bb", EASY)
30
+ orphan = Index("dd", 1, "aa", EASY)
31
+ # Pass the entries in hash order rather than height order.
32
+ ordered = select_main_chain([orphan, tip, genesis, main])
33
+ self.assertEqual([block.hash for block in ordered], ["aa", "bb", "cc"])
34
+
35
+ def test_total_work_beats_a_longer_weaker_chain(self):
36
+ genesis = Index("aa", 0, ZERO, EASY)
37
+ hard = Index("00", 1, "aa", HARD)
38
+ first = Index("b1", 1, "aa", EASY)
39
+ second = Index("b2", 2, "b1", EASY)
40
+ third = Index("b3", 3, "b2", EASY)
41
+ ordered = select_main_chain([third, second, first, hard, genesis])
42
+ self.assertEqual([block.hash for block in ordered], ["aa", "00"])
43
+
44
+ def test_more_work_wins_at_the_same_height(self):
45
+ genesis = Index("aa", 0, ZERO, EASY)
46
+ # "ff" would win a hash tie-break, so this only passes when work differs.
47
+ easy = Index("ff", 1, "aa", EASY)
48
+ hard = Index("00", 1, "aa", HARD)
49
+ ordered = select_main_chain([easy, hard, genesis])
50
+ self.assertEqual([block.hash for block in ordered], ["aa", "00"])
51
+
52
+ def test_height_window_skips_blocks_without_data(self):
53
+ blocks = []
54
+ for height in range(6):
55
+ prev = "h%d" % (height - 1) if height else ZERO
56
+ blocks.append(Index("h%d" % height, height, prev, EASY))
57
+ blocks[2].file = -1
58
+ ordered = select_main_chain(blocks)
59
+
60
+ forward = [block.height for block in iter_chain_indexes(ordered, 0, 5)]
61
+ self.assertEqual(forward, [0, 1, 3, 4])
62
+
63
+ backward = [block.height for block in iter_chain_indexes(ordered, 5, 0)]
64
+ self.assertEqual(backward, [5, 4, 3, 1, 0])
65
+
66
+ def test_cycle_does_not_hang(self):
67
+ first = Index("aa", 1, "bb", EASY)
68
+ second = Index("bb", 2, "aa", EASY)
69
+ ordered = select_main_chain([first, second])
70
+ self.assertGreater(len(ordered), 0)
71
+ self.assertLessEqual(len(ordered), 2)
72
+
73
+ def test_missing_transaction_returns_none(self):
74
+ class EmptyDB(object):
75
+ def get(self, key):
76
+ return None
77
+
78
+ blockchain = Blockchain("blocks")
79
+ self.assertIsNone(blockchain.get_transaction("ab" * 32, EmptyDB()))