You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base58_tests.cpp 9.3KB

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