base58_tests.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // ECOin - Copyright (c) - 2014/2021 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include <boost/test/unit_test.hpp>
  3. #include "json/json_spirit_reader_template.h"
  4. #include "json/json_spirit_writer_template.h"
  5. #include "json/json_spirit_utils.h"
  6. #include "base58.h"
  7. #include "util.h"
  8. using namespace json_spirit;
  9. extern Array read_json(const std::string& filename);
  10. BOOST_AUTO_TEST_SUITE(base58_tests)
  11. // Goal: test low-level base58 encoding functionality
  12. BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
  13. {
  14. Array tests = read_json("base58_encode_decode.json");
  15. BOOST_FOREACH(Value& tv, tests)
  16. {
  17. Array test = tv.get_array();
  18. std::string strTest = write_string(tv, false);
  19. if (test.size() < 2) // Allow for extra stuff (useful for comments)
  20. {
  21. BOOST_ERROR("Bad test: " << strTest);
  22. continue;
  23. }
  24. std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
  25. std::string base58string = test[1].get_str();
  26. BOOST_CHECK_MESSAGE(
  27. EncodeBase58(&sourcedata[0], &sourcedata[sourcedata.size()]) == base58string,
  28. strTest);
  29. }
  30. }
  31. // Goal: test low-level base58 decoding functionality
  32. BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
  33. {
  34. Array tests = read_json("base58_encode_decode.json");
  35. std::vector<unsigned char> result;
  36. BOOST_FOREACH(Value& tv, tests)
  37. {
  38. Array test = tv.get_array();
  39. std::string strTest = write_string(tv, false);
  40. if (test.size() < 2) // Allow for extra stuff (useful for comments)
  41. {
  42. BOOST_ERROR("Bad test: " << strTest);
  43. continue;
  44. }
  45. std::vector<unsigned char> expected = ParseHex(test[0].get_str());
  46. std::string base58string = test[1].get_str();
  47. BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest);
  48. BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
  49. }
  50. BOOST_CHECK(!DecodeBase58("invalid", result));
  51. }
  52. // Visitor to check address type
  53. class TestAddrTypeVisitor : public boost::static_visitor<bool>
  54. {
  55. private:
  56. std::string exp_addrType;
  57. public:
  58. TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
  59. bool operator()(const CKeyID &id) const
  60. {
  61. return (exp_addrType == "pubkey");
  62. }
  63. bool operator()(const CScriptID &id) const
  64. {
  65. return (exp_addrType == "script");
  66. }
  67. bool operator()(const CNoDestination &no) const
  68. {
  69. return (exp_addrType == "none");
  70. }
  71. };
  72. // Visitor to check address payload
  73. class TestPayloadVisitor : public boost::static_visitor<bool>
  74. {
  75. private:
  76. std::vector<unsigned char> exp_payload;
  77. public:
  78. TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
  79. bool operator()(const CKeyID &id) const
  80. {
  81. uint160 exp_key(exp_payload);
  82. return exp_key == id;
  83. }
  84. bool operator()(const CScriptID &id) const
  85. {
  86. uint160 exp_key(exp_payload);
  87. return exp_key == id;
  88. }
  89. bool operator()(const CNoDestination &no) const
  90. {
  91. return exp_payload.size() == 0;
  92. }
  93. };
  94. // Goal: check that parsed keys match test payload
  95. BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
  96. {
  97. Array tests = read_json("base58_keys_valid.json");
  98. std::vector<unsigned char> result;
  99. CEcoinSecret secret;
  100. CEcoinAddress addr;
  101. // Save global state
  102. bool fTestNet_stored = fTestNet;
  103. BOOST_FOREACH(Value& tv, tests)
  104. {
  105. Array test = tv.get_array();
  106. std::string strTest = write_string(tv, false);
  107. if (test.size() < 3) // Allow for extra stuff (useful for comments)
  108. {
  109. BOOST_ERROR("Bad test: " << strTest);
  110. continue;
  111. }
  112. std::string exp_base58string = test[0].get_str();
  113. std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
  114. const Object &metadata = test[2].get_obj();
  115. bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
  116. bool isTestnet = find_value(metadata, "isTestnet").get_bool();
  117. fTestNet = isTestnet; // Override testnet flag
  118. if(isPrivkey)
  119. {
  120. bool isCompressed = find_value(metadata, "isCompressed").get_bool();
  121. // Must be valid private key
  122. // Note: CEcoinSecret::SetString tests isValid, whereas CEcoinAddress does not!
  123. BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
  124. BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
  125. bool fCompressedOut = false;
  126. CSecret privkey = secret.GetSecret(fCompressedOut);
  127. BOOST_CHECK_MESSAGE(fCompressedOut == isCompressed, "compressed mismatch:" + strTest);
  128. BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
  129. // Private key must be invalid public key
  130. addr.SetString(exp_base58string);
  131. BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest);
  132. }
  133. else
  134. {
  135. std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
  136. // Must be valid public key
  137. BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest);
  138. BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest);
  139. BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest);
  140. CTxDestination dest = addr.Get();
  141. BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest);
  142. // Public key must be invalid private key
  143. secret.SetString(exp_base58string);
  144. BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest);
  145. }
  146. }
  147. // Restore global state
  148. fTestNet = fTestNet_stored;
  149. }
  150. // Goal: check that generated keys match test vectors
  151. BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
  152. {
  153. Array tests = read_json("base58_keys_valid.json");
  154. std::vector<unsigned char> result;
  155. // Save global state
  156. bool fTestNet_stored = fTestNet;
  157. BOOST_FOREACH(Value& tv, tests)
  158. {
  159. Array test = tv.get_array();
  160. std::string strTest = write_string(tv, false);
  161. if (test.size() < 3) // Allow for extra stuff (useful for comments)
  162. {
  163. BOOST_ERROR("Bad test: " << strTest);
  164. continue;
  165. }
  166. std::string exp_base58string = test[0].get_str();
  167. std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
  168. const Object &metadata = test[2].get_obj();
  169. bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
  170. bool isTestnet = find_value(metadata, "isTestnet").get_bool();
  171. fTestNet = isTestnet; // Override testnet flag
  172. if(isPrivkey)
  173. {
  174. bool isCompressed = find_value(metadata, "isCompressed").get_bool();
  175. CEcoinSecret secret;
  176. secret.SetSecret(CSecret(exp_payload.begin(), exp_payload.end()), isCompressed);
  177. BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
  178. }
  179. else
  180. {
  181. std::string exp_addrType = find_value(metadata, "addrType").get_str();
  182. CTxDestination dest;
  183. if(exp_addrType == "pubkey")
  184. {
  185. dest = CKeyID(uint160(exp_payload));
  186. }
  187. else if(exp_addrType == "script")
  188. {
  189. dest = CScriptID(uint160(exp_payload));
  190. }
  191. else if(exp_addrType == "none")
  192. {
  193. dest = CNoDestination();
  194. }
  195. else
  196. {
  197. BOOST_ERROR("Bad addrtype: " << strTest);
  198. continue;
  199. }
  200. CEcoinAddress addrOut;
  201. BOOST_CHECK_MESSAGE(boost::apply_visitor(CEcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest);
  202. BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest);
  203. }
  204. }
  205. // Visiting a CNoDestination must fail
  206. CEcoinAddress dummyAddr;
  207. CTxDestination nodest = CNoDestination();
  208. BOOST_CHECK(!boost::apply_visitor(CEcoinAddressVisitor(&dummyAddr), nodest));
  209. // Restore global state
  210. fTestNet = fTestNet_stored;
  211. }
  212. // Goal: check that base58 parsing code is robust against a variety of corrupted data
  213. BOOST_AUTO_TEST_CASE(base58_keys_invalid)
  214. {
  215. Array tests = read_json("base58_keys_invalid.json"); // Negative testcases
  216. std::vector<unsigned char> result;
  217. CEcoinSecret secret;
  218. CEcoinAddress addr;
  219. BOOST_FOREACH(Value& tv, tests)
  220. {
  221. Array test = tv.get_array();
  222. std::string strTest = write_string(tv, false);
  223. if (test.size() < 1) // Allow for extra stuff (useful for comments)
  224. {
  225. BOOST_ERROR("Bad test: " << strTest);
  226. continue;
  227. }
  228. std::string exp_base58string = test[0].get_str();
  229. // must be invalid as public and as private key
  230. addr.SetString(exp_base58string);
  231. BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest);
  232. secret.SetString(exp_base58string);
  233. BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
  234. }
  235. }
  236. BOOST_AUTO_TEST_SUITE_END()