LCOV - code coverage report
Current view: top level - src/test - base58_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 136 154 88.3 %
Date: 2025-02-23 09:33:43 Functions: 12 12 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2014 The Bitcoin Core developers
       2             : // Copyright (c) 2017-2022 The PIVX Core developers
       3             : // Distributed under the MIT/X11 software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "base58.h"
       7             : 
       8             : #include "data/base58_encode_decode.json.h"
       9             : #include "data/base58_keys_invalid.json.h"
      10             : #include "data/base58_keys_valid.json.h"
      11             : 
      12             : #include "key.h"
      13             : #include "key_io.h"
      14             : #include "uint256.h"
      15             : #include "utilstrencodings.h"
      16             : #include "test/test_pivx.h"
      17             : #include "util/vector.h"
      18             : 
      19             : #include <boost/test/unit_test.hpp>
      20             : 
      21             : #include <univalue.h>
      22             : 
      23             : extern UniValue read_json(const std::string& jsondata);
      24             : 
      25             : BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup)
      26             : 
      27             : // Goal: test low-level base58 encoding functionality
      28           2 : BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
      29             : {
      30           3 :     UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
      31          13 :     for (unsigned int idx = 0; idx < tests.size(); idx++) {
      32          12 :         UniValue test = tests[idx];
      33          24 :         std::string strTest = test.write();
      34          12 :         if (test.size() < 2) // Allow for extra stuff (useful for comments)
      35             :         {
      36           0 :             BOOST_ERROR("Bad test: " << strTest);
      37           0 :             continue;
      38             :         }
      39          24 :         std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
      40          24 :         std::string base58string = test[1].get_str();
      41          26 :         BOOST_CHECK_MESSAGE(
      42             :                     EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
      43             :                     strTest);
      44             :     }
      45           1 : }
      46             : 
      47             : // Goal: test low-level base58 decoding functionality
      48           2 : BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
      49             : {
      50           2 :     UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
      51           2 :     std::vector<unsigned char> result;
      52             : 
      53          13 :     for (unsigned int idx = 0; idx < tests.size(); idx++) {
      54          12 :         UniValue test = tests[idx];
      55          24 :         std::string strTest = test.write();
      56          12 :         if (test.size() < 2) // Allow for extra stuff (useful for comments)
      57             :         {
      58           0 :             BOOST_ERROR("Bad test: " << strTest);
      59           0 :             continue;
      60             :         }
      61          24 :         std::vector<unsigned char> expected = ParseHex(test[0].get_str());
      62          24 :         std::string base58string = test[1].get_str();
      63          24 :         BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest);
      64          24 :         BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
      65             :     }
      66             : 
      67           2 :     BOOST_CHECK(!DecodeBase58("invalid", result, 100));
      68           2 :     BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100));
      69           3 :     BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100));
      70             : 
      71           3 :     BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100));
      72           3 :     BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100));
      73           3 :     BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100));
      74           3 :     BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100));
      75             : 
      76             :     // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end.
      77           2 :     BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3));
      78           2 :     BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3));
      79           2 :     std::vector<unsigned char> expected = ParseHex("971a55");
      80           2 :     BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
      81             : 
      82           4 :     BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100));
      83           4 :     BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100));
      84           4 :     BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100));
      85           4 :     BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100));
      86           1 : }
      87             : 
      88             : // Visitor to check address type
      89          26 : class TestAddrTypeVisitor : public boost::static_visitor<bool>
      90             : {
      91             : private:
      92             :     std::string exp_addrType;
      93             : public:
      94          26 :     explicit TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
      95          13 :     bool operator()(const CKeyID &id) const
      96             :     {
      97          13 :         return (exp_addrType == "pubkey");
      98             :     }
      99           0 :     bool operator()(const CExchangeKeyID &id) const
     100             :     {
     101           0 :         return (exp_addrType == "exchangepubkey");
     102             :     }
     103          13 :     bool operator()(const CScriptID &id) const
     104             :     {
     105          13 :         return (exp_addrType == "script");
     106             :     }
     107           0 :     bool operator()(const CNoDestination &no) const
     108             :     {
     109           0 :         return (exp_addrType == "none");
     110             :     }
     111             : };
     112             : 
     113             : // Visitor to check address payload
     114             : class TestPayloadVisitor : public boost::static_visitor<bool>
     115             : {
     116             : private:
     117             :     std::vector<unsigned char> exp_payload;
     118             : public:
     119             :     explicit TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
     120             :     bool operator()(const CKeyID &id) const
     121             :     {
     122             :         uint160 exp_key(exp_payload);
     123             :         return exp_key == id;
     124             :     }
     125             :     bool operator()(const CScriptID &id) const
     126             :     {
     127             :         uint160 exp_key(exp_payload);
     128             :         return exp_key == id;
     129             :     }
     130             :     bool operator()(const CNoDestination &no) const
     131             :     {
     132             :         return exp_payload.size() == 0;
     133             :     }
     134             : };
     135             : 
     136             : // Goal: check that parsed keys match test payload
     137           2 : BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
     138             : {
     139           2 :     UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
     140           2 :     std::vector<unsigned char> result;
     141           2 :     CKey privkey;
     142           2 :     CTxDestination destination;
     143           1 :     SelectParams(CBaseChainParams::MAIN);
     144             : 
     145          51 :     for (unsigned int idx = 0; idx < tests.size(); idx++) {
     146          50 :         UniValue test = tests[idx];
     147         100 :         std::string strTest = test.write();
     148          50 :         if (test.size() < 3) // Allow for extra stuff (useful for comments)
     149             :         {
     150           0 :             BOOST_ERROR("Bad test: " << strTest);
     151           0 :             continue;
     152             :         }
     153         100 :         std::string exp_base58string = test[0].get_str();
     154         100 :         std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
     155          50 :         const UniValue &metadata = test[2].get_obj();
     156          50 :         bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
     157          50 :         bool isTestnet = find_value(metadata, "isTestnet").get_bool();
     158          50 :         if (isTestnet)
     159          24 :             SelectParams(CBaseChainParams::TESTNET);
     160             :         else
     161          26 :             SelectParams(CBaseChainParams::MAIN);
     162          50 :         if(isPrivkey) {
     163          24 :             bool isCompressed = find_value(metadata, "isCompressed").get_bool();
     164             :             // Must be valid private key
     165          48 :             privkey = KeyIO::DecodeSecret(exp_base58string);
     166          72 :             BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
     167          72 :             BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
     168          96 :             BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
     169             : 
     170             :             // Private key must be invalid public key
     171          48 :             destination = DecodeDestination(exp_base58string);
     172          72 :             BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
     173             :         } else {
     174          78 :             std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
     175             :             // Must be valid public key
     176          52 :             destination = DecodeDestination(exp_base58string);
     177          78 :             BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
     178          78 :             BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
     179         104 :             BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
     180             : 
     181             :             // Public key must be invalid private key
     182          52 :             privkey = KeyIO::DecodeSecret(exp_base58string);
     183          78 :             BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest);
     184             :         }
     185             :     }
     186           1 : }
     187             : 
     188             : // Goal: check that generated keys match test vectors
     189           2 : BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
     190             : {
     191           2 :     UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
     192           2 :     std::vector<unsigned char> result;
     193             : 
     194          51 :     for (unsigned int idx = 0; idx < tests.size(); idx++) {
     195          50 :         UniValue test = tests[idx];
     196         100 :         std::string strTest = test.write();
     197          50 :         if (test.size() < 3) // Allow for extra stuff (useful for comments)
     198             :         {
     199           0 :             BOOST_ERROR("Bad test: " << strTest);
     200           0 :             continue;
     201             :         }
     202         100 :         std::string exp_base58string = test[0].get_str();
     203         100 :         std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
     204          50 :         const UniValue &metadata = test[2].get_obj();
     205          50 :         bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
     206          50 :         bool isTestnet = find_value(metadata, "isTestnet").get_bool();
     207          50 :         if (isTestnet)
     208          24 :             SelectParams(CBaseChainParams::TESTNET);
     209             :         else
     210          26 :             SelectParams(CBaseChainParams::MAIN);
     211          50 :         if(isPrivkey)
     212             :         {
     213          24 :             bool isCompressed = find_value(metadata, "isCompressed").get_bool();
     214          48 :             CKey key;
     215          24 :             key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
     216          24 :             assert(key.IsValid());
     217          96 :             BOOST_CHECK_MESSAGE(KeyIO::EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest);
     218             :         }
     219             :         else
     220             :         {
     221          78 :             std::string exp_addrType = find_value(metadata, "addrType").get_str();
     222          52 :             CTxDestination dest;
     223          26 :             if(exp_addrType == "pubkey")
     224             :             {
     225          13 :                 dest = CKeyID(uint160(exp_payload));
     226             :             }
     227          13 :             else if(exp_addrType == "script")
     228             :             {
     229          13 :                 dest = CScriptID(uint160(exp_payload));
     230             :             }
     231           0 :             else if(exp_addrType == "none")
     232             :             {
     233           0 :                 dest = CNoDestination();
     234             :             }
     235             :             else
     236             :             {
     237           0 :                 BOOST_ERROR("Bad addrtype: " << strTest);
     238           0 :                 continue;
     239             :             }
     240          52 :             std::string address = EncodeDestination(dest);
     241          78 :             BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
     242             :         }
     243             :     }
     244             : 
     245           1 :     SelectParams(CBaseChainParams::MAIN);
     246           1 : }
     247             : 
     248             : // Goal: check that base58 parsing code is robust against a variety of corrupted data
     249           2 : BOOST_AUTO_TEST_CASE(base58_keys_invalid)
     250             : {
     251           2 :     UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
     252           2 :     std::vector<unsigned char> result;
     253           2 :     CKey privkey;
     254           2 :     CTxDestination destination;
     255             : 
     256          51 :     for (unsigned int idx = 0; idx < tests.size(); idx++) {
     257          50 :         UniValue test = tests[idx];
     258         100 :         std::string strTest = test.write();
     259          50 :         if (test.size() < 1) // Allow for extra stuff (useful for comments)
     260             :         {
     261           0 :             BOOST_ERROR("Bad test: " << strTest);
     262           0 :             continue;
     263             :         }
     264         100 :         std::string exp_base58string = test[0].get_str();
     265             : 
     266             :         // must be invalid as public and as private key
     267         100 :         destination = DecodeDestination(exp_base58string);
     268         150 :         BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
     269         100 :         privkey = KeyIO::DecodeSecret(exp_base58string);
     270         150 :         BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey:" + strTest);
     271             :     }
     272           1 : }
     273             : 
     274           2 : BOOST_AUTO_TEST_CASE(base58_random_encode_decode)
     275             : {
     276        1001 :     for (int n = 0; n < 1000; ++n) {
     277        1000 :         unsigned int len = 1 + InsecureRandBits(8);
     278        1000 :         unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0;
     279        3000 :         auto data = Cat(std::vector<unsigned char>(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes));
     280        2000 :         auto encoded = EncodeBase58Check(data);
     281        2000 :         std::vector<unsigned char> decoded;
     282        1000 :         auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len));
     283        2000 :         BOOST_CHECK(!ok_too_small);
     284        1000 :         auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len));
     285        2000 :         BOOST_CHECK(ok);
     286        2000 :         BOOST_CHECK(data == decoded);
     287             :     }
     288           1 : }
     289             : 
     290             : BOOST_AUTO_TEST_SUITE_END()
     291             : 

Generated by: LCOV version 1.14