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