Coverage for tests/comm/test_bitcoin.py: 100%
80 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23from unittest import TestCase
24from comm.bitcoin import (
25 get_tx_hash_for_unsigned_tx,
26 get_signature_hash_for_p2sh_input,
27 get_block_hash_as_int,
28 get_merkle_root,
29 get_unsigned_tx,
30 get_tx_hash,
31 get_tx_version,
32)
34import logging
36logging.disable(logging.CRITICAL)
38SAMPLE_1_UNSIGNED = """
39010000000187ace6cb83436c876dd2233dfadf537068ecb1b9a0000d3590c9072d46d2bf4c000000006e0
40000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362
41634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a65423
427da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff02b030eb0b000000001976a914f6
43b794549667efd57f083e018c1a4726c73ccb4388ac00301a1e0100000017a914896ed9f3446d51b5510f7
44f0b6ef81b2bde55140e8700000000
45""".replace("\n", "").replace("\r", "")
47SAMPLE_1_PARTIALLY_SIGNED = """
48010000000187ace6cb83436c876dd2233dfadf537068ecb1b9a0000d3590c9072d46d2bf4c00000000b60
49048304502210084ddb5c7c6b57405c4068fa6634c8dd736d53ebf1ea878ad962026eecb86b57502206988
50e345a0af65ca5a72418580896028cdd1834404fe97774234725e494a18f101004c69522102cd53fc53a07
51f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a
528c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b1
5332b8ca630f245df04db53aeffffffff02b030eb0b000000001976a914f6b794549667efd57f083e018c1a
544726c73ccb4388ac00301a1e0100000017a914896ed9f3446d51b5510f7f0b6ef81b2bde55140e8700000
55000
56""".replace("\n", "").replace("\r", "")
58SAMPLE_1_PARTIALLY_SIGNED_HASH = (
59 "e9ceb9260f258f13a029ad212f08719d8656b564cf4108b86199d83969de7c2e")
60SAMPLE_1_UNSIGNED_HASH = (
61 "f7e2b314eb12bd13481c5325ae3839fdbe5c508dbc7e24a44aac5e9992d07718")
62SAMPLE_1_SIGHASH_INPUT0 = (
63 "db4ca3f81a68996e7c51e7e138524a53ab9770410f628199dc6ab5a5bac73e5c")
65SAMPLE_2_UNSIGNED = """
6601000000025cc070156cd1487db3c0c0d22affe09f208950bbe1bd6d2bc4a7c65749cee0c1000000006e000000
674c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae
689cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0
69878657175b132b8ca630f245df04db53aeffffffffbe6be3ddaf27e9609c9b319db9c97eae36e88dcda760ff4e
700b77ccd086c7f4c6000000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e8
7111a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5
72946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff021044d871020000
73001976a914f6b794549667efd57f083e018c1a4726c73ccb4388ac008d380c0100000017a914896ed9f3446d51
74b5510f7f0b6ef81b2bde55140e8700000000
75""".replace("\n", "").replace("\r", "")
77SAMPLE_2_PARTIALLY_SIGNED = """
7801000000025cc070156cd1487db3c0c0d22affe09f208950bbe1bd6d2bc4a7c65749cee0c100000000b5004730
794402203743f5bc4632b46fa261de4bdeaa1b3d617bd9dcaaf9fb3f9272c95c7f7b8b11022055464b2dae639037
80b42b3bc559bcf7580a7aa3e421c1511a6419ae7338029cc601004c69522102cd53fc53a07f211641a677d250f6
81de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809ba
82b643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffff
83ffffbe6be3ddaf27e9609c9b319db9c97eae36e88dcda760ff4e0b77ccd086c7f4c600000000b5004730440220
84046b94168f1181a81ac8b4d7543b6392743d81a5b0c494db95be12ce2d9ba91002201dc5ad6c9f7b6171678a78
85a910fbd59150d8f86ef39f3244a82761cbf87670c801004c69522102cd53fc53a07f211641a677d250f6de99ca
86f620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab64307
872d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff02
881044d871020000001976a914f6b794549667efd57f083e018c1a4726c73ccb4388ac008d380c0100000017a914
89896ed9f3446d51b5510f7f0b6ef81b2bde55140e8700000000
90""".replace("\n", "").replace("\r", "")
92SAMPLE_2_PARTIALLY_SIGNED_HASH = (
93 "39ae0553e91565866acc81a61da52cd87702ed2b4632f52bba19e3d9ba7e46ce")
94SAMPLE_2_UNSIGNED_HASH = (
95 "f8318d6c071efd988411cd63ee7d3fbf736d5ed38ba0679fa0be7acddb1bddda")
96SAMPLE_2_SIGHASH_INPUT0 = (
97 "ef95325f9c819476a54097abe466374f8293f26637af3686833d73dc29be2093")
98SAMPLE_2_SIGHASH_INPUT1 = (
99 "20d8b8e413868c08e987ae11c7280b95293c05f0e191ef847a6060593b759814")
101SAMPLE_3_UNSIGNED = """
1020100000001ec7281cfc9e59db5c1e69fc294846572bd3d78091bd0c6fdc21c2209c500556a000000004b000000
103475221031da807c71c2f303b7f409dd2605b297ac494a563be3b9ca5f52d95a43d183cc521036bb9eab797eadc
1048b697f0e82a01d01cabbfaaca37e5bafc06fdc6fdd38af894a52aeffffffff01b01c661b0c0000001976a914f7
105ee4511563688dce9b5d74e07cc9932b8e01f2288ac00000000
106""".replace("\n", "").replace("\r", "")
108SAMPLE_3_UNSIGNED_HASH = (
109 "ea73e8a9a6b98da8ba3f47d28f94af6818c8e906937a95f302263920b4cda792")
110SAMPLE_3_SIGHASH_INPUT0 = (
111 "5aeb5a6bbecda560b297da643559ce3c1b727076fb2ba1118c94d5906617aa19")
113SAMPLE_TX_V2 = """
11402000000010ac6236167ee3c88aee00f0f3b89f92b43a84797a71b6b62ffd354f11ef8c2f000000000de000000
115004cd8645221024c759affafc5589872d218ca30377e6d97211c039c375672c169ba76ce7fad6a21031f4aa494
1163fa2b731cd99c551d6992021555877b3b32c125385600fbc1b89c2a92103767a0994daa8babee7215b2371916d
11709fc1158de3c23feeefaae2dfe5baf483053670132b275522102132685d71b0109fecef0160f1efcab0187eff9
11816f4d472289741bff2666d0e1c2102ed498022f9d618a96f272b1990a640d9f24fb97d2648f8716f9ee22dc008
119eba721036f66639295ca8e4294c24d63e3fbc11247f6ba6a27b6b4de9a3492f414152d9b5368aeffffffff0294
1208e4b00000000001976a9140a4f09cbd39d5d8072b24385e1a9eb1c84ae544688acf83d6e0b0000000017a914ba
121053351893c7495e0c75d5abacb3ed886cf1ff88700000000
122""".replace("\n", "").replace("\r", "")
125class TestBitcoin(TestCase):
126 def test_signed_unsigned_different_sample1(self):
127 self.assertNotEqual(SAMPLE_1_UNSIGNED, SAMPLE_1_PARTIALLY_SIGNED)
129 def test_get_tx_hash_for_unsigned_sample1(self):
130 self.assertEqual(SAMPLE_1_UNSIGNED_HASH,
131 get_tx_hash_for_unsigned_tx(SAMPLE_1_UNSIGNED))
133 def test_get_tx_hash_sample1(self):
134 self.assertEqual(SAMPLE_1_UNSIGNED_HASH, get_tx_hash(SAMPLE_1_UNSIGNED))
136 def test_get_tx_hash_sample1_signed(self):
137 self.assertEqual(SAMPLE_1_PARTIALLY_SIGNED_HASH,
138 get_tx_hash(SAMPLE_1_PARTIALLY_SIGNED))
140 def test_get_tx_for_unsigned_hash_partially_signed_sample1(self):
141 self.assertEqual(SAMPLE_1_UNSIGNED_HASH,
142 get_tx_hash_for_unsigned_tx(SAMPLE_1_PARTIALLY_SIGNED))
144 def test_get_sighash_unsigned_sample1(self):
145 self.assertEqual(
146 SAMPLE_1_SIGHASH_INPUT0,
147 get_signature_hash_for_p2sh_input(SAMPLE_1_UNSIGNED, 0),
148 )
150 def test_get_sighash_partially_signed_sample1(self):
151 self.assertEqual(
152 SAMPLE_1_SIGHASH_INPUT0,
153 get_signature_hash_for_p2sh_input(SAMPLE_1_PARTIALLY_SIGNED, 0),
154 )
156 def test_get_unsigned_sample1(self):
157 self.assertEqual(SAMPLE_1_UNSIGNED, get_unsigned_tx(SAMPLE_1_PARTIALLY_SIGNED))
159 def test_get_unsigned_sample2(self):
160 self.assertEqual(SAMPLE_2_UNSIGNED, get_unsigned_tx(SAMPLE_2_PARTIALLY_SIGNED))
162 def test_signed_unsigned_different_sample2(self):
163 self.assertNotEqual(SAMPLE_2_UNSIGNED, SAMPLE_2_PARTIALLY_SIGNED)
165 def test_get_tx_hash_sample2(self):
166 self.assertEqual(SAMPLE_2_UNSIGNED_HASH, get_tx_hash(SAMPLE_2_UNSIGNED))
168 def test_get_tx_hash_sample2_signed(self):
169 self.assertEqual(SAMPLE_2_PARTIALLY_SIGNED_HASH,
170 get_tx_hash(SAMPLE_2_PARTIALLY_SIGNED))
172 def test_get_tx_hash_for_unsigned_sample2(self):
173 self.assertEqual(SAMPLE_2_UNSIGNED_HASH,
174 get_tx_hash_for_unsigned_tx(SAMPLE_2_UNSIGNED))
176 def test_get_tx_hash_for_unsigned_partially_signed_sample2(self):
177 self.assertEqual(SAMPLE_2_UNSIGNED_HASH,
178 get_tx_hash_for_unsigned_tx(SAMPLE_2_PARTIALLY_SIGNED))
180 def test_get_sighash_unsigned_sample2_input0(self):
181 self.assertEqual(
182 SAMPLE_2_SIGHASH_INPUT0,
183 get_signature_hash_for_p2sh_input(SAMPLE_2_UNSIGNED, 0),
184 )
186 def test_get_sighash_unsigned_sample2_input1(self):
187 self.assertEqual(
188 SAMPLE_2_SIGHASH_INPUT1,
189 get_signature_hash_for_p2sh_input(SAMPLE_2_UNSIGNED, 1),
190 )
192 def test_get_tx_hash_not_signed_sample3(self):
193 self.assertEqual(SAMPLE_3_UNSIGNED_HASH,
194 get_tx_hash_for_unsigned_tx(SAMPLE_3_UNSIGNED))
196 def test_get_sighash_unsigned_sample3_input0(self):
197 self.assertEqual(
198 SAMPLE_3_SIGHASH_INPUT0,
199 get_signature_hash_for_p2sh_input(SAMPLE_3_UNSIGNED, 0),
200 )
202 # Taken from https://www.blockchain.com/btc/block/624147
203 # Raw taken from first 80 bytes of https://blockchain.info/block/624147?format=hex
204 def test_get_block_hash_as_int_sample1(self):
205 expected = int.from_bytes(
206 bytes.fromhex(
207 "0000000000000000000cca3342146c09d6907c7f9ca57523b9bd86fa30899894"),
208 byteorder="big",
209 signed=True,
210 )
211 actual = get_block_hash_as_int(
212 "0000c0208426f01512acfecc731b6087fc29bbaecceb90343bd40800000000000000000032ec"
213 "a913ca8c61beb3776f9701938c4c4ae1e400a574d3e56c5fbac67ab17677a18d865e413b1417"
214 "cef412c9"
215 )
216 self.assertEqual(actual, expected)
218 # Taken from https://www.blockchain.com/btc/block/564123
219 # Raw taken from first 80 bytes of https://blockchain.info/block/564123?format=hex
220 def test_get_block_hash_as_int_sample2(self):
221 expected = int.from_bytes(
222 bytes.fromhex(
223 "0000000000000000001561d124184ae540941dbeaf34841f0ec7d9301f5bc487"),
224 byteorder="big",
225 signed=True,
226 )
227 actual = get_block_hash_as_int(
228 "00004020b274d71decd77d50ef2465a0e06de23d4e6ac2940f6629000000000000000000b0c4"
229 "11fb72cf1406ea03172719ca3da827e256868b24b3d40db188759e8c66e7309a6f5c886f2e17"
230 "04d58fd3"
231 )
232 self.assertEqual(actual, expected)
234 def test_get_block_hash_as_int_malformed(self):
235 with self.assertRaises(ValueError):
236 get_block_hash_as_int("aabbcc")
238 # Taken from https://www.blockchain.com/btc/block/421895
239 # Raw taken from first 80 bytes of https://blockchain.info/block/421895?format=hex
240 def test_get_merkle_root_sample1(self):
241 actual = get_merkle_root(
242 "00000020d03543c0470f715b5eb9a77963b1442ea86e7deb89151d0200000000000000002ee2"
243 "fbed876dc6559e75dddca7de82d619a442ce7df93e2c5f507f926a8257e690cc925769260518"
244 "6e4b7622"
245 )
246 self.assertEqual(
247 actual, "2ee2fbed876dc6559e75dddca7de82d619a442ce7df93e2c5f507f926a8257e6")
249 # Taken from https://www.blockchain.com/btc/block/564123
250 # Raw taken from first 80 bytes of https://blockchain.info/block/564123?format=hex
251 def test_get_merkle_root_sample2(self):
252 actual = get_merkle_root(
253 "00004020b274d71decd77d50ef2465a0e06de23d4e6ac2940f6629000000000000000000b0c4"
254 "11fb72cf1406ea03172719ca3da827e256868b24b3d40db188759e8c66e7309a6f5c886f2e17"
255 "04d58fd3"
256 )
257 self.assertEqual(
258 actual, "b0c411fb72cf1406ea03172719ca3da827e256868b24b3d40db188759e8c66e7")
260 def test_get_merkle_root_malformed(self):
261 with self.assertRaises(ValueError):
262 get_merkle_root("aabbcc")
264 def test_get_tx_version_a(self):
265 self.assertEqual(1, get_tx_version(SAMPLE_1_UNSIGNED))
267 def test_get_tx_version_b(self):
268 self.assertEqual(2, get_tx_version(SAMPLE_TX_V2))