/ src / test / compress_tests.cpp
compress_tests.cpp
  1  // Copyright (c) 2012-2021 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 <stdint.h>
 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(g_insecure_rand_ctx.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()