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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. BOOST_FOREACH(Value& tv, tests)
  101. {
  102. Array test = tv.get_array();
  103. std::string strTest = write_string(tv, false);
  104. if (test.size() < 3) // Allow for extra stuff (useful for comments)
  105. {
  106. BOOST_ERROR("Bad test: " << strTest);
  107. continue;
  108. }
  109. std::string exp_base58string = test[0].get_str();
  110. std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
  111. const Object &metadata = test[2].get_obj();
  112. bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
  113. bool isTestnet = find_value(metadata, "isTestnet").get_bool();
  114. if (isTestnet)
  115. SelectParams(CChainParams::TESTNET);
  116. else
  117. SelectParams(CChainParams::MAIN);
  118. if(isPrivkey)
  119. {
  120. bool isCompressed = find_value(metadata, "isCompressed").get_bool();
  121. // Must be valid private key
  122. // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not!
  123. BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
  124. BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
  125. CKey privkey = secret.GetKey();
  126. BOOST_CHECK_MESSAGE(privkey.IsCompressed() == 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. SelectParams(CChainParams::MAIN);
  147. }
  148. // Goal: check that generated keys match test vectors
  149. BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
  150. {
  151. Array tests = read_json("base58_keys_valid.json");
  152. std::vector<unsigned char> result;
  153. BOOST_FOREACH(Value& tv, tests)
  154. {
  155. Array test = tv.get_array();
  156. std::string strTest = write_string(tv, false);
  157. if (test.size() < 3) // Allow for extra stuff (useful for comments)
  158. {
  159. BOOST_ERROR("Bad test: " << strTest);
  160. continue;
  161. }
  162. std::string exp_base58string = test[0].get_str();
  163. std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
  164. const Object &metadata = test[2].get_obj();
  165. bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
  166. bool isTestnet = find_value(metadata, "isTestnet").get_bool();
  167. if (isTestnet)
  168. SelectParams(CChainParams::TESTNET);
  169. else
  170. SelectParams(CChainParams::MAIN);
  171. if(isPrivkey)
  172. {
  173. bool isCompressed = find_value(metadata, "isCompressed").get_bool();
  174. CKey key;
  175. key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
  176. assert(key.IsValid());
  177. CBitcoinSecret secret;
  178. secret.SetKey(key);
  179. BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
  180. }
  181. else
  182. {
  183. std::string exp_addrType = find_value(metadata, "addrType").get_str();
  184. CTxDestination dest;
  185. if(exp_addrType == "pubkey")
  186. {
  187. dest = CKeyID(uint160(exp_payload));
  188. }
  189. else if(exp_addrType == "script")
  190. {
  191. dest = CScriptID(uint160(exp_payload));
  192. }
  193. else if(exp_addrType == "none")
  194. {
  195. dest = CNoDestination();
  196. }
  197. else
  198. {
  199. BOOST_ERROR("Bad addrtype: " << strTest);
  200. continue;
  201. }
  202. CBitcoinAddress addrOut;
  203. BOOST_CHECK_MESSAGE(boost::apply_visitor(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest);
  204. BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest);
  205. }
  206. }
  207. // Visiting a CNoDestination must fail
  208. CBitcoinAddress dummyAddr;
  209. CTxDestination nodest = CNoDestination();
  210. BOOST_CHECK(!boost::apply_visitor(CBitcoinAddressVisitor(&dummyAddr), nodest));
  211. SelectParams(CChainParams::MAIN);
  212. }
  213. // Goal: check that base58 parsing code is robust against a variety of corrupted data
  214. BOOST_AUTO_TEST_CASE(base58_keys_invalid)
  215. {
  216. Array tests = read_json("base58_keys_invalid.json"); // Negative testcases
  217. std::vector<unsigned char> result;
  218. CBitcoinSecret secret;
  219. CBitcoinAddress addr;
  220. BOOST_FOREACH(Value& tv, tests)
  221. {
  222. Array test = tv.get_array();
  223. std::string strTest = write_string(tv, false);
  224. if (test.size() < 1) // Allow for extra stuff (useful for comments)
  225. {
  226. BOOST_ERROR("Bad test: " << strTest);
  227. continue;
  228. }
  229. std::string exp_base58string = test[0].get_str();
  230. // must be invalid as public and as private key
  231. addr.SetString(exp_base58string);
  232. BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest);
  233. secret.SetString(exp_base58string);
  234. BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
  235. }
  236. }
  237. BOOST_AUTO_TEST_SUITE_END()