compress_tests.cpp
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()