compress_tests.cpp raw
1 // Copyright (c) 2012-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <compressor.h>
6 #include <script/script.h>
7 #include <test/util/random.h>
8 #include <test/util/setup_common.h>
9
10 #include <cstdint>
11
12 #include <boost/test/unit_test.hpp>
13
14 // amounts 0.00000001 .. 0.00100000
15 #define NUM_MULTIPLES_UNIT 100000
16
17 // amounts 0.01 .. 100.00
18 #define NUM_MULTIPLES_CENT 10000
19
20 // amounts 1 .. 10000
21 #define NUM_MULTIPLES_1BTC 10000
22
23 // amounts 50 .. 21000000
24 #define NUM_MULTIPLES_50BTC 420000
25
26 BOOST_FIXTURE_TEST_SUITE(compress_tests, BasicTestingSetup)
27
28 bool static TestEncode(uint64_t in) {
29 return in == DecompressAmount(CompressAmount(in));
30 }
31
32 bool static TestDecode(uint64_t in) {
33 return in == CompressAmount(DecompressAmount(in));
34 }
35
36 bool static TestPair(uint64_t dec, uint64_t enc) {
37 return CompressAmount(dec) == enc &&
38 DecompressAmount(enc) == dec;
39 }
40
41 BOOST_AUTO_TEST_CASE(compress_amounts)
42 {
43 BOOST_CHECK(TestPair( 0, 0x0));
44 BOOST_CHECK(TestPair( 1, 0x1));
45 BOOST_CHECK(TestPair( CENT, 0x7));
46 BOOST_CHECK(TestPair( COIN, 0x9));
47 BOOST_CHECK(TestPair( 50*COIN, 0x32));
48 BOOST_CHECK(TestPair(21000000*COIN, 0x1406f40));
49
50 for (uint64_t i = 1; i <= NUM_MULTIPLES_UNIT; i++)
51 BOOST_CHECK(TestEncode(i));
52
53 for (uint64_t i = 1; i <= NUM_MULTIPLES_CENT; i++)
54 BOOST_CHECK(TestEncode(i * CENT));
55
56 for (uint64_t i = 1; i <= NUM_MULTIPLES_1BTC; i++)
57 BOOST_CHECK(TestEncode(i * COIN));
58
59 for (uint64_t i = 1; i <= NUM_MULTIPLES_50BTC; i++)
60 BOOST_CHECK(TestEncode(i * 50 * COIN));
61
62 for (uint64_t i = 0; i < 100000; i++)
63 BOOST_CHECK(TestDecode(i));
64 }
65
66 BOOST_AUTO_TEST_CASE(compress_script_to_ckey_id)
67 {
68 // case CKeyID
69 CKey key = GenerateRandomKey();
70 CPubKey pubkey = key.GetPubKey();
71
72 CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
73 BOOST_CHECK_EQUAL(script.size(), 25U);
74
75 CompressedScript out;
76 bool done = CompressScript(script, out);
77 BOOST_CHECK_EQUAL(done, true);
78
79 // Check compressed script
80 BOOST_CHECK_EQUAL(out.size(), 21U);
81 BOOST_CHECK_EQUAL(out[0], 0x00);
82 BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 3, 20), 0); // compare the 20 relevant chars of the CKeyId in the script
83 }
84
85 BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id)
86 {
87 // case CScriptID
88 CScript script, redeemScript;
89 script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
90 BOOST_CHECK_EQUAL(script.size(), 23U);
91
92 CompressedScript out;
93 bool done = CompressScript(script, out);
94 BOOST_CHECK_EQUAL(done, true);
95
96 // Check compressed script
97 BOOST_CHECK_EQUAL(out.size(), 21U);
98 BOOST_CHECK_EQUAL(out[0], 0x01);
99 BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 20), 0); // compare the 20 relevant chars of the CScriptId in the script
100 }
101
102 BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id)
103 {
104 CKey key = GenerateRandomKey(); // case compressed PubKeyID
105
106 CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33)
107 BOOST_CHECK_EQUAL(script.size(), 35U);
108
109 CompressedScript out;
110 bool done = CompressScript(script, out);
111 BOOST_CHECK_EQUAL(done, true);
112
113 // Check compressed script
114 BOOST_CHECK_EQUAL(out.size(), 33U);
115 BOOST_CHECK_EQUAL(memcmp(out.data(), script.data() + 1, 1), 0);
116 BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 32), 0); // compare the 32 chars of the compressed CPubKey
117 }
118
119 BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
120 {
121 CKey key = GenerateRandomKey(/*compressed=*/false); // case uncompressed PubKeyID
122 CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65)
123 BOOST_CHECK_EQUAL(script.size(), 67U); // 1 char code + 65 char pubkey + OP_CHECKSIG
124
125 CompressedScript out;
126 bool done = CompressScript(script, out);
127 BOOST_CHECK_EQUAL(done, true);
128
129 // Check compressed script
130 BOOST_CHECK_EQUAL(out.size(), 33U);
131 BOOST_CHECK_EQUAL(memcmp(out.data() + 1, script.data() + 2, 32), 0); // first 32 chars of CPubKey are copied into out[1:]
132 BOOST_CHECK_EQUAL(out[0], 0x04 | (script[65] & 0x01)); // least significant bit (lsb) of last char of pubkey is mapped into out[0]
133 }
134
135 BOOST_AUTO_TEST_CASE(compress_p2pk_scripts_not_on_curve)
136 {
137 XOnlyPubKey x_not_on_curve;
138 do {
139 x_not_on_curve = XOnlyPubKey(m_rng.randbytes(32));
140 } while (x_not_on_curve.IsFullyValid());
141
142 // Check that P2PK script with uncompressed pubkey [=> OP_PUSH65 <0x04 .....> OP_CHECKSIG]
143 // which is not fully valid (i.e. point is not on curve) can't be compressed
144 std::vector<unsigned char> pubkey_raw(65, 0);
145 pubkey_raw[0] = 4;
146 std::copy(x_not_on_curve.begin(), x_not_on_curve.end(), &pubkey_raw[1]);
147 CPubKey pubkey_not_on_curve(pubkey_raw);
148 assert(pubkey_not_on_curve.IsValid());
149 assert(!pubkey_not_on_curve.IsFullyValid());
150 CScript script = CScript() << ToByteVector(pubkey_not_on_curve) << OP_CHECKSIG;
151 BOOST_CHECK_EQUAL(script.size(), 67U);
152
153 CompressedScript out;
154 bool done = CompressScript(script, out);
155 BOOST_CHECK_EQUAL(done, false);
156
157 // Check that compressed P2PK script with uncompressed pubkey that is not fully
158 // valid (i.e. x coordinate of the pubkey is not on curve) can't be decompressed
159 CompressedScript compressed_script(x_not_on_curve.begin(), x_not_on_curve.end());
160 for (unsigned int compression_id : {4, 5}) {
161 CScript uncompressed_script;
162 bool success = DecompressScript(uncompressed_script, compression_id, compressed_script);
163 BOOST_CHECK_EQUAL(success, false);
164 }
165 }
166
167 BOOST_AUTO_TEST_SUITE_END()
168