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