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 10KB

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