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,48 @@
1
+ import unittest
2
+ from binascii import a2b_hex
3
+
4
+ from blockchain_parser.index import DBBlockIndex
5
+ from blockchain_parser.index import DBTransactionIndex
6
+
7
+
8
+ class TestDBIndex(unittest.TestCase):
9
+ def test_from_hex(self):
10
+ key_str = "0000000000000000169cdec8dcfa2e408f59e0d50b1a228f65d8f548" \
11
+ "0f990000"
12
+ value_str = "88927193a7021d8160804aaa89fc0185b6e81e02000000fb759231" \
13
+ "e1fa5f80c3508e3a59ebf301930257d04aa4920700000000000000" \
14
+ "00c11c6bc67af8264be7979db45043f5f5c1e8d2060082af4ce795" \
15
+ "7658a22147e30bf97f54747b1b187d1eac41"
16
+
17
+ value_hex = a2b_hex(value_str)
18
+ idx = DBBlockIndex(key_str, value_hex)
19
+
20
+ self.assertEqual(idx.hash, "0000000000000000169cdec8dcfa2e408f59e0d50"
21
+ "b1a228f65d8f5480f990000")
22
+ self.assertEqual(idx.height, 332802)
23
+ self.assertEqual(idx.status, 29)
24
+ self.assertEqual(idx.n_tx, 352)
25
+ self.assertEqual(idx.file, 202)
26
+ self.assertEqual(idx.data_pos, 90357377)
27
+ self.assertEqual(idx.undo_pos, 13497502)
28
+ self.assertEqual(idx.version, 2)
29
+ self.assertEqual(idx.nonce, 1101799037)
30
+ self.assertEqual(idx.prev_hash, "00000000000000000792a44ad057029301f3e"
31
+ "b593a8e50c3805ffae1319275fb")
32
+ self.assertEqual(idx.merkle_root, "e34721a2587695e74caf820006d2e8c1f5f"
33
+ "54350b49d97e74b26f87ac66b1cc1")
34
+
35
+
36
+ class TestDBTransactionIndex(unittest.TestCase):
37
+ def test_from_hex(self):
38
+ key_str = "70ad7da56decc86b8a58ac53dbde792c9e97552cdaafd37312af7c4" \
39
+ "d5c7d0cc1"
40
+ value_str = "9071938b980ba4bf39"
41
+
42
+ value_hex = a2b_hex(value_str)
43
+ idx = DBTransactionIndex(key_str, value_hex)
44
+
45
+ self.assertEqual(idx.hash, key_str)
46
+ self.assertEqual(idx.blockfile_no, 2289)
47
+ self.assertEqual(idx.block_offset, 614457)
48
+ self.assertEqual(idx.file_offset, 42142859)
@@ -0,0 +1,66 @@
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.output import Output
16
+
17
+
18
+ class TestOutput(unittest.TestCase):
19
+ def test_pubkeyhash_from_hex(self):
20
+ raw_output = "01000000000000001976a91432ba382cf668657bae15ee0a97fa87" \
21
+ "f12e1bc89f88ac"
22
+ output = Output.from_hex(a2b_hex(raw_output))
23
+ self.assertTrue(output.is_pubkeyhash())
24
+ self.assertEqual("pubkeyhash", output.type)
25
+ self.assertEqual(1, output.value)
26
+ self.assertEqual(1, len(output.addresses))
27
+
28
+ def test_pubkey_from_hex(self):
29
+ raw_output = "0100000000000000232102c0993f639534d348e1dca30566491e6c" \
30
+ "b11c14afa13ec244c05396a9839aeb17ac"
31
+ output = Output.from_hex(a2b_hex(raw_output))
32
+ self.assertTrue(output.is_pubkey())
33
+ self.assertEqual("pubkey", output.type)
34
+ self.assertEqual(1, len(output.addresses))
35
+
36
+ def test_p2sh_from_hex(self):
37
+ raw_output = "010000000000000017a91471c5c3727fac8dbace94bd38cf8ac16a" \
38
+ "034a794787"
39
+ output = Output.from_hex(a2b_hex(raw_output))
40
+
41
+ self.assertTrue(output.is_p2sh())
42
+ self.assertEqual("p2sh", output.type)
43
+ self.assertEqual(1, len(output.addresses))
44
+
45
+ def test_return_from_hex(self):
46
+ raw_output = "01000000000000002a6a2846610000000024958857cc0da391b" \
47
+ "7b2bf61bcba59bb9ee438873f902c25da4c079e53d0c55fe991"
48
+ output = Output.from_hex(a2b_hex(raw_output))
49
+ self.assertTrue(output.is_return())
50
+ self.assertEqual("OP_RETURN", output.type)
51
+ self.assertEqual(0, len(output.addresses))
52
+
53
+ def test_multisig_from_hex(self):
54
+ raw_output = "0100000000000000475121025cd452979d4d5e928d47c3581bb287" \
55
+ "41b2cf9c54185e7d563a663707b00d956d2102ff99d00aa9d195b9" \
56
+ "3732254def8bfe80a786a7973ef8e63afd8d2a65e97b6c3b52ae"
57
+ output = Output.from_hex(a2b_hex(raw_output))
58
+ self.assertTrue(output.is_multisig())
59
+ self.assertEqual("multisig", output.type)
60
+ self.assertEqual(2, len(output.addresses))
61
+
62
+ def test_unknown_from_hex(self):
63
+ raw_output = "01000000000000000151"
64
+ output = Output.from_hex(a2b_hex(raw_output))
65
+ self.assertEqual("unknown", output.type)
66
+ self.assertEqual(0, len(output.addresses))
@@ -0,0 +1,171 @@
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
+ from blockchain_parser.script import Script
15
+
16
+
17
+ class TestScript(unittest.TestCase):
18
+ def test_op_return_script(self):
19
+ case1 = "6a"
20
+ script = Script.from_hex(a2b_hex(case1))
21
+ self.assertEqual("OP_RETURN", script.value)
22
+ self.assertFalse(script.is_pubkey())
23
+ self.assertFalse(script.is_multisig())
24
+ self.assertFalse(script.is_p2sh())
25
+ self.assertFalse(script.is_p2wpkh())
26
+ self.assertFalse(script.is_p2wsh())
27
+ self.assertFalse(script.is_pubkeyhash())
28
+ self.assertFalse(script.is_unknown())
29
+ self.assertTrue(script.is_return())
30
+ self.assertIs(script.is_p2tr(), False)
31
+
32
+ def test_unknown_script(self):
33
+ case = "40"
34
+ script = Script.from_hex(a2b_hex(case))
35
+ self.assertEqual("INVALID_SCRIPT", script.value)
36
+ self.assertFalse(script.is_pubkey())
37
+ self.assertFalse(script.is_multisig())
38
+ self.assertFalse(script.is_p2sh())
39
+ self.assertFalse(script.is_p2wpkh())
40
+ self.assertFalse(script.is_p2wsh())
41
+ self.assertFalse(script.is_pubkeyhash())
42
+ self.assertTrue(script.is_unknown())
43
+ self.assertFalse(script.is_return())
44
+ self.assertIs(script.is_p2tr(), False)
45
+
46
+ case = ""
47
+ script = Script.from_hex(a2b_hex(case))
48
+ self.assertEqual("", script.value)
49
+ self.assertFalse(script.is_pubkey())
50
+ self.assertFalse(script.is_multisig())
51
+ self.assertFalse(script.is_p2sh())
52
+ self.assertFalse(script.is_p2wpkh())
53
+ self.assertFalse(script.is_p2wsh())
54
+ self.assertFalse(script.is_pubkeyhash())
55
+ self.assertTrue(script.is_unknown())
56
+ self.assertFalse(script.is_return())
57
+ self.assertIs(script.is_p2tr(), False)
58
+
59
+ def test_multisig_script(self):
60
+ case = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"
61
+ script = Script.from_hex(a2b_hex(case))
62
+ self.assertFalse(script.is_pubkey())
63
+ self.assertTrue(script.is_multisig())
64
+ self.assertFalse(script.is_p2sh())
65
+ self.assertFalse(script.is_p2wpkh())
66
+ self.assertFalse(script.is_p2wsh())
67
+ self.assertFalse(script.is_pubkeyhash())
68
+ self.assertFalse(script.is_unknown())
69
+ self.assertFalse(script.is_return())
70
+ self.assertIs(script.is_p2tr(), False)
71
+
72
+ def test_p2sh_script(self):
73
+ case = "a91428ad3e63dcae36e5010527578e2eef0e9eeaf3e487"
74
+ script = Script.from_hex(a2b_hex(case))
75
+ self.assertFalse(script.is_pubkey())
76
+ self.assertFalse(script.is_multisig())
77
+ self.assertTrue(script.is_p2sh())
78
+ self.assertFalse(script.is_p2wpkh())
79
+ self.assertFalse(script.is_p2wsh())
80
+ self.assertFalse(script.is_pubkeyhash())
81
+ self.assertFalse(script.is_unknown())
82
+ self.assertFalse(script.is_return())
83
+ self.assertIs(script.is_p2tr(), False)
84
+
85
+ def test_p2wpkh_script(self):
86
+ case = "0014c958269b5b6469b6e4b87de1062028ad3bb83cc2"
87
+ script = Script.from_hex(a2b_hex(case))
88
+ self.assertFalse(script.is_pubkey())
89
+ self.assertFalse(script.is_multisig())
90
+ self.assertFalse(script.is_p2sh())
91
+ self.assertTrue(script.is_p2wpkh())
92
+ self.assertFalse(script.is_p2wsh())
93
+ self.assertFalse(script.is_pubkeyhash())
94
+ self.assertFalse(script.is_unknown())
95
+ self.assertFalse(script.is_return())
96
+ self.assertIs(script.is_p2tr(), False)
97
+
98
+ def test_p2wsh_script(self):
99
+ case = "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d"
100
+ script = Script.from_hex(a2b_hex(case))
101
+ self.assertFalse(script.is_p2sh())
102
+ self.assertFalse(script.is_multisig())
103
+ self.assertFalse(script.is_p2sh())
104
+ self.assertFalse(script.is_p2wpkh())
105
+ self.assertTrue(script.is_p2wsh())
106
+ self.assertFalse(script.is_pubkeyhash())
107
+ self.assertFalse(script.is_unknown())
108
+ self.assertFalse(script.is_return())
109
+ self.assertIs(script.is_p2tr(), False)
110
+
111
+ def test_pubkeyhash_script(self):
112
+ case = "76a914e9629ef6f5b82564a9b2ecae6c288c56fb33710888ac"
113
+ script = Script.from_hex(a2b_hex(case))
114
+ self.assertFalse(script.is_pubkey())
115
+ self.assertFalse(script.is_multisig())
116
+ self.assertFalse(script.is_p2sh())
117
+ self.assertFalse(script.is_p2wpkh())
118
+ self.assertFalse(script.is_p2wsh())
119
+ self.assertTrue(script.is_pubkeyhash())
120
+ self.assertFalse(script.is_unknown())
121
+ self.assertFalse(script.is_return())
122
+ self.assertIs(script.is_p2tr(), False)
123
+
124
+ def test_pubkey_script(self):
125
+ script = Script.from_hex(a2b_hex("4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"))
126
+ self.assertTrue(script.is_pubkey())
127
+ self.assertFalse(script.is_multisig())
128
+ self.assertFalse(script.is_p2sh())
129
+ self.assertFalse(script.is_p2wpkh())
130
+ self.assertFalse(script.is_p2wsh())
131
+ self.assertFalse(script.is_pubkeyhash())
132
+ self.assertFalse(script.is_unknown())
133
+ self.assertFalse(script.is_return())
134
+ self.assertIs(script.is_p2tr(), False)
135
+
136
+ def test_taproot_script(self):
137
+ # https://blockstream.info/tx/33e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac98799036?expand
138
+ # Second output
139
+ script = Script.from_hex(a2b_hex("5120a37c3903c8d0db6512e2b40b0dffa05e5a3ab73603ce8c9c4b7771e5412328f9"))
140
+ self.assertFalse(script.is_pubkey())
141
+ self.assertFalse(script.is_multisig())
142
+ self.assertFalse(script.is_p2sh())
143
+ self.assertFalse(script.is_p2wpkh())
144
+ self.assertFalse(script.is_p2wsh())
145
+ self.assertFalse(script.is_pubkeyhash())
146
+ self.assertFalse(script.is_unknown())
147
+ self.assertFalse(script.is_return())
148
+ self.assertTrue(script.is_p2tr())
149
+
150
+ def test_p2tr_rejects_non_taproot_witness(self):
151
+ short = Script.from_hex(bytes([0x51, 0x02, 0x00, 0x01]))
152
+ self.assertIs(short.is_p2tr(), False)
153
+ self.assertTrue(short.is_unknown())
154
+
155
+ extra = Script.from_hex(bytes([0x51, 0x20]) + (b"\x11" * 32) + bytes([0xac]))
156
+ self.assertIs(extra.is_p2tr(), False)
157
+
158
+ def test_invalid_pubkeyhash_length_does_not_raise(self):
159
+ script = Script.from_hex(bytes([0x20]) + (b"\x00" * 24))
160
+ self.assertIs(script.is_pubkeyhash(), False)
161
+ self.assertTrue(script.is_unknown())
162
+
163
+ def test_malformed_multisig_does_not_raise(self):
164
+ pubkey = bytes([0x21, 0x02]) + (b"\xab" * 32)
165
+ too_few_keys = Script.from_hex(bytes([0x60]) + pubkey + pubkey + pubkey)
166
+ self.assertIs(too_few_keys.is_multisig(), False)
167
+ self.assertTrue(too_few_keys.is_unknown())
168
+
169
+ mismatched_count = Script.from_hex(bytes([0x51]) + pubkey + bytes([0x52, 0xae]))
170
+ self.assertIs(mismatched_count.is_multisig(), False)
171
+ self.assertTrue(mismatched_count.is_unknown())
@@ -0,0 +1,138 @@
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 os
13
+ import unittest
14
+ from binascii import a2b_hex, b2a_hex
15
+ from blockchain_parser.transaction import Transaction
16
+
17
+ from .utils import read_test_data
18
+
19
+ class TestTransaction(unittest.TestCase):
20
+ def test_rbf(self):
21
+ data = a2b_hex("01000000019222bbb054bb9f94571dfe769af5866835f2a97e8839"
22
+ "59fa757de4064bed8bca01000000035101b1000000000100000000"
23
+ "00000000016a01000000")
24
+ tx = Transaction(data)
25
+ self.assertTrue(tx.uses_replace_by_fee())
26
+
27
+ coinbase = a2b_hex("01000000010000000000000000000000000000000000000000"
28
+ "000000000000000000000000ffffffff4203c8e405fabe6d6d"
29
+ "98b0e98e3809941f1fd8cafe7c8236e27b8d1a776b1835aa54"
30
+ "8bb84fe5b5f3d7010000000000000002650300aaa757eb0000"
31
+ "002f736c7573682f0000000001baa98396000000001976a914"
32
+ "7c154ed1dc59609e3d26abb2df2ea3d587cd8c4188ac000000"
33
+ "00")
34
+ tx = Transaction(coinbase)
35
+ self.assertTrue(tx.is_coinbase())
36
+ self.assertFalse(tx.uses_replace_by_fee())
37
+
38
+ def test_bip69(self):
39
+ non_compliant = read_test_data("bip69_false.txt")
40
+ tx = Transaction(non_compliant)
41
+ self.assertFalse(tx.uses_bip69())
42
+
43
+ compliant = read_test_data("bip69_true.txt")
44
+ tx = Transaction(compliant)
45
+ self.assertTrue(tx.uses_bip69())
46
+
47
+ def test_bip69_sorts_scripts_as_bytes(self):
48
+ def build(scripts):
49
+ tx_input = b"\x11" * 32 + b"\x00\x00\x00\x00" + b"\x00" + b"\xff" * 4
50
+ outputs = b""
51
+ for script in scripts:
52
+ outputs += (b"\x01" + b"\x00" * 7) + bytes([len(script)]) + script
53
+ raw = (
54
+ b"\x01\x00\x00\x00"
55
+ + b"\x01" + tx_input
56
+ + bytes([len(scripts)]) + outputs
57
+ + b"\x00\x00\x00\x00"
58
+ )
59
+ return Transaction(raw)
60
+
61
+ # 0x6a < 0x76, so OP_RETURN before OP_DUP is the BIP69 byte order.
62
+ self.assertTrue(build([b"\x6a", b"\x76"]).uses_bip69())
63
+ self.assertFalse(build([b"\x76", b"\x6a"]).uses_bip69())
64
+
65
+ def test_bech32_p2wpkh(self):
66
+ tx = Transaction(read_test_data("bech32_p2wpkh.txt"))
67
+ self.assertEqual(["3BBqfnaPbgi5KWECWdFpvryUfw7QatWy37"], [a.address for a in tx.outputs[0].addresses])
68
+ self.assertEqual(["bc1q4z0874xmfxe3xeqknulgnqhukhfjwh5tvjrr2x"], [a.address for a in tx.outputs[1].addresses])
69
+
70
+ def test_bech32_p2wsh(self):
71
+ tx = Transaction(read_test_data("bech32_p2wsh.txt"))
72
+ self.assertEqual(["3GMKKFPNUg13VktgihUD8QfXVQRBdoDNDf"], [a.address for a in tx.outputs[0].addresses])
73
+ self.assertEqual(["bc1qday7wsftyv4r6qkpn8907s8fy3kexkny0xwrd8d4wlk06zffzyuqpp629n"], [a.address for a in tx.outputs[1].addresses])
74
+
75
+ def test_segwit(self):
76
+ tx = Transaction(read_test_data("segwit.txt"))
77
+ self.assertTrue(tx.is_segwit)
78
+ id = "22116f1d76ab425ddc6d10d184331e70e080dd6275d7aa90237ceb648dc38224"
79
+ self.assertTrue(tx.txid == id)
80
+ h = "1eac09f372a8c13bb7dea6bd66ee71a6bcc469b57b35c1e394ad7eb7c107c507"
81
+ self.assertTrue(tx.hash == h)
82
+
83
+ segwit_input = tx.inputs[0]
84
+ self.assertTrue(len(segwit_input.witnesses) == 4)
85
+ self.assertTrue(len(segwit_input.witnesses[0]) == 0)
86
+
87
+ wit_1 = "3045022100bc2ba8808127f8a74beed6dfa1b9fe54675c55aab85a61d7" \
88
+ "a74c15b993e67e5f02204dada4e15f0b4e659dae7bf0d0f648010d1f2b" \
89
+ "665f587a35eb6f22e44194952301"
90
+
91
+ wit_2 = "3045022100f4c7ec7c2064fe2cc4389733ac0a57d8080a62180a004b02" \
92
+ "a19b89267113a17f022004ee9fdb081359c549ee42ffb58279363563ea" \
93
+ "f0191cd8b2b0ceebf62146b50b01"
94
+
95
+ wit_3 = "5221022b003d276bce58bef509bdcd9cf7e156f0eae18e1175815282e6" \
96
+ "5e7da788bb5b21035c58f2f60ecf38c9c8b9d1316b662627ec672f5fd9" \
97
+ "12b1a2cc28d0b9b00575fd2103c96d495bfdd5ba4145e3e046fee45e84" \
98
+ "a8a48ad05bd8dbb395c011a32cf9f88053ae"
99
+
100
+ parsed_1 = b2a_hex(segwit_input.witnesses[1]).decode("utf-8")
101
+ parsed_2 = b2a_hex(segwit_input.witnesses[2]).decode("utf-8")
102
+ parsed_3 = b2a_hex(segwit_input.witnesses[3]).decode("utf-8")
103
+
104
+ self.assertEqual(parsed_1, wit_1)
105
+ self.assertEqual(parsed_2, wit_2)
106
+ self.assertEqual(parsed_3, wit_3)
107
+
108
+ def test_vsize(self):
109
+ segwit_tx = Transaction(read_test_data("size_segwit.txt"))
110
+ non_segwit_tx = Transaction(read_test_data("size_non_segwit.txt"))
111
+
112
+ self.assertEqual(non_segwit_tx.vsize, non_segwit_tx.size)
113
+ self.assertEqual(non_segwit_tx.vsize, 189)
114
+
115
+ self.assertNotEqual(segwit_tx.vsize, segwit_tx.size)
116
+ self.assertEqual(segwit_tx.vsize, 208)
117
+ self.assertEqual(segwit_tx.size, 373)
118
+
119
+ def test_large(self):
120
+ data = read_test_data("large_tx.txt")
121
+
122
+ tx = Transaction(data)
123
+ self.assertTrue(
124
+ tx.hash == "29a3efd3ef04f9153d47a990bd7b048a4b2d213daa"
125
+ "a5fb8ed670fb85f13bdbcf")
126
+ self.assertTrue(tx.size == len(tx.hex))
127
+
128
+ def test_incomplete(self):
129
+ data = read_test_data("invalid_tx.txt")
130
+
131
+ self.assertRaises(Exception, Transaction, data)
132
+
133
+ def test_unknown_scripts(self):
134
+ data = read_test_data("scripts_invalid.txt")
135
+ tx = Transaction(data)
136
+
137
+ for output in tx.outputs:
138
+ self.assertEqual([], output.addresses)
@@ -0,0 +1,70 @@
1
+ import unittest
2
+
3
+ from blockchain_parser.undo import BlockUndo, decompress_script
4
+
5
+
6
+ def _coin(height, program, amount_code=1):
7
+ # Even height codes are non-coinbase. Height > 0 carries a dummy 0x00.
8
+ raw = bytes([height * 2])
9
+ if height > 0:
10
+ raw += b"\x00"
11
+ return raw + bytes([amount_code, 0x00]) + program
12
+
13
+
14
+ class TestUndo(unittest.TestCase):
15
+ def test_p2pkh_script_and_amount(self):
16
+ program = b"\x11" * 20
17
+ undo = BlockUndo(bytes([1, 1]) + _coin(1, program))
18
+ spent = undo.spends[0].outputs[0]
19
+ self.assertEqual(spent.height, 1)
20
+ self.assertFalse(spent.is_coinbase)
21
+ self.assertEqual(spent.amt, 1)
22
+ self.assertEqual(
23
+ spent.script.hex,
24
+ b"\x76\xa9\x14" + program + b"\x88\xac",
25
+ )
26
+ self.assertTrue(spent.script.is_pubkeyhash())
27
+
28
+ def test_height_zero_has_no_dummy_byte(self):
29
+ program = b"\x22" * 20
30
+ undo = BlockUndo(bytes([1, 1]) + _coin(0, program))
31
+ spent = undo.spends[0].outputs[0]
32
+ self.assertEqual(spent.height, 0)
33
+ self.assertEqual(spent.amt, 1)
34
+ self.assertEqual(spent.script.hex[3:23], program)
35
+
36
+ def test_second_output_stays_aligned(self):
37
+ first = _coin(1, b"\x11" * 20)
38
+ second = _coin(2, b"\x22" * 20)
39
+ undo = BlockUndo(bytes([1, 2]) + first + second)
40
+ outputs = undo.spends[0].outputs
41
+ self.assertEqual(len(outputs), 2)
42
+ self.assertEqual(outputs[1].height, 2)
43
+ self.assertEqual(outputs[1].amt, 1)
44
+ self.assertEqual(outputs[1].script.hex[3:23], b"\x22" * 20)
45
+
46
+ def test_p2sh_and_compressed_pubkey(self):
47
+ program = b"\x44" * 20
48
+ self.assertEqual(
49
+ decompress_script(bytes([1]) + program),
50
+ b"\xa9\x14" + program + b"\x87",
51
+ )
52
+ x_only = b"\x11" * 32
53
+ self.assertEqual(
54
+ decompress_script(bytes([2]) + x_only),
55
+ bytes([0x21, 0x02]) + x_only + b"\xac",
56
+ )
57
+
58
+ def test_uncompressed_pubkey_on_secp256k1(self):
59
+ x_only = bytes.fromhex(
60
+ "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
61
+ )
62
+ y_coord = bytes.fromhex(
63
+ "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
64
+ )
65
+ script = decompress_script(bytes([0x04]) + x_only)
66
+ self.assertEqual(script, bytes([0x41, 0x04]) + x_only + y_coord + b"\xac")
67
+
68
+ def test_raw_script(self):
69
+ raw = b"\x51"
70
+ self.assertEqual(decompress_script(bytes([6 + len(raw)]) + raw), raw)
@@ -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 binascii import b2a_hex, a2b_hex
14
+
15
+ from blockchain_parser import utils
16
+
17
+
18
+ class TestUtils(unittest.TestCase):
19
+ def test_btc_ripemd160(self):
20
+ data = a2b_hex("02218AD6CDC632E7AE7D04472374311CEBBBBF0AB540D2D08C3400BB844C654231")
21
+ ripe = utils.btc_ripemd160(data)
22
+ expected = b"5238c71458e464d9ff90299abca4a1d7b9cb76ab"
23
+ self.assertEqual(b2a_hex(ripe), expected)
24
+
25
+ def test_format_hash(self):
26
+ data = a2b_hex("deadbeef")
27
+ self.assertEqual(utils.format_hash(data), "efbeadde")
28
+
29
+ def test_decode_uint32(self):
30
+ uint32_dict = {
31
+ "01000000": 1,
32
+ "000000ff": 4278190080
33
+ }
34
+
35
+ for uint32, value in uint32_dict.items():
36
+ self.assertEqual(utils.decode_uint32(a2b_hex(uint32)), value)
37
+
38
+ def test_decode_uint64(self):
39
+ uint64_dict = {
40
+ "0100000000000000": 1,
41
+ "00000000000000ff": 18374686479671623680
42
+ }
43
+
44
+ for uint64, value in uint64_dict.items():
45
+ self.assertEqual(utils.decode_uint64(a2b_hex(uint64)), value)
46
+
47
+ def test_decode_compactsize(self):
48
+ case1 = a2b_hex("fa")
49
+ self.assertEqual(utils.decode_compactsize(case1), (250, 1))
50
+ case2 = a2b_hex("fd0100")
51
+ self.assertEqual(utils.decode_compactsize(case2), (1, 3))
52
+ case3 = a2b_hex("fe01000000")
53
+ self.assertEqual(utils.decode_compactsize(case3), (1, 5))
54
+ case4 = a2b_hex("ff0100000000000000")
55
+ self.assertEqual(utils.decode_compactsize(case4), (1, 9))
@@ -0,0 +1,9 @@
1
+ import os
2
+ from binascii import a2b_hex
3
+
4
+ dir_path = os.path.dirname(os.path.realpath(__file__))
5
+
6
+
7
+ def read_test_data(filename):
8
+ with open(os.path.join(dir_path, "data/", filename)) as f:
9
+ return a2b_hex(f.read().strip())