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.
- blockchain_parser/__init__.py +12 -0
- blockchain_parser/address.py +83 -0
- blockchain_parser/block.py +104 -0
- blockchain_parser/block_header.py +90 -0
- blockchain_parser/blockchain.py +302 -0
- blockchain_parser/index.py +81 -0
- blockchain_parser/input.py +78 -0
- blockchain_parser/output.py +145 -0
- blockchain_parser/script.py +164 -0
- blockchain_parser/tests/__init__.py +10 -0
- blockchain_parser/tests/data/bech32_p2wpkh.txt +1 -0
- blockchain_parser/tests/data/bech32_p2wsh.txt +1 -0
- blockchain_parser/tests/data/bip69_false.txt +1 -0
- blockchain_parser/tests/data/bip69_true.txt +1 -0
- blockchain_parser/tests/data/genesis_block.txt +1 -0
- blockchain_parser/tests/data/invalid_tx.txt +1 -0
- blockchain_parser/tests/data/large_tx.txt +1 -0
- blockchain_parser/tests/data/scripts_invalid.txt +1 -0
- blockchain_parser/tests/data/segwit.txt +1 -0
- blockchain_parser/tests/data/size_non_segwit.txt +1 -0
- blockchain_parser/tests/data/size_segwit.txt +1 -0
- blockchain_parser/tests/test_address.py +45 -0
- blockchain_parser/tests/test_block.py +55 -0
- blockchain_parser/tests/test_blockchain.py +79 -0
- blockchain_parser/tests/test_index.py +48 -0
- blockchain_parser/tests/test_output.py +66 -0
- blockchain_parser/tests/test_script.py +171 -0
- blockchain_parser/tests/test_transaction.py +138 -0
- blockchain_parser/tests/test_undo.py +70 -0
- blockchain_parser/tests/test_utils.py +55 -0
- blockchain_parser/tests/utils.py +9 -0
- blockchain_parser/transaction.py +196 -0
- blockchain_parser/undo.py +198 -0
- blockchain_parser/utils.py +152 -0
- blockchain_parser/utils_taproot.py +143 -0
- blockchain_parser_2-0.1.6.dist-info/METADATA +143 -0
- blockchain_parser_2-0.1.6.dist-info/RECORD +40 -0
- blockchain_parser_2-0.1.6.dist-info/WHEEL +5 -0
- blockchain_parser_2-0.1.6.dist-info/licenses/LICENSE +177 -0
- 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))
|