PIVX Core  5.6.99
P2P Digital Currency
base58_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin Core developers
2 // Copyright (c) 2017-2022 The PIVX Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "base58.h"
7 
11 
12 #include "key.h"
13 #include "key_io.h"
14 #include "uint256.h"
15 #include "utilstrencodings.h"
16 #include "test/test_pivx.h"
17 #include "util/vector.h"
18 
19 #include <boost/test/unit_test.hpp>
20 
21 #include <univalue.h>
22 
23 extern UniValue read_json(const std::string& jsondata);
24 
26 
27 // Goal: test low-level base58 encoding functionality
28 BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
29 {
30  UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
31  for (unsigned int idx = 0; idx < tests.size(); idx++) {
32  UniValue test = tests[idx];
33  std::string strTest = test.write();
34  if (test.size() < 2) // Allow for extra stuff (useful for comments)
35  {
36  BOOST_ERROR("Bad test: " << strTest);
37  continue;
38  }
39  std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
40  std::string base58string = test[1].get_str();
41  BOOST_CHECK_MESSAGE(
42  EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
43  strTest);
44  }
45 }
46 
47 // Goal: test low-level base58 decoding functionality
48 BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
49 {
50  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  for (unsigned int idx = 0; idx < tests.size(); idx++) {
54  UniValue test = tests[idx];
55  std::string strTest = test.write();
56  if (test.size() < 2) // Allow for extra stuff (useful for comments)
57  {
58  BOOST_ERROR("Bad test: " << strTest);
59  continue;
60  }
61  std::vector<unsigned char> expected = ParseHex(test[0].get_str());
62  std::string base58string = test[1].get_str();
63  BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest);
64  BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
65  }
66 
67  BOOST_CHECK(!DecodeBase58("invalid", result, 100));
68  BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100));
69  BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100));
70 
71  BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100));
72  BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100));
73  BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100));
74  BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100));
75 
76  // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end.
77  BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3));
78  BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3));
79  std::vector<unsigned char> expected = ParseHex("971a55");
80  BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
81 
82  BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100));
83  BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100));
84  BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100));
85  BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100));
86 }
87 
88 // Visitor to check address type
89 class TestAddrTypeVisitor : public boost::static_visitor<bool>
90 {
91 private:
92  std::string exp_addrType;
93 public:
94  explicit TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
95  bool operator()(const CKeyID &id) const
96  {
97  return (exp_addrType == "pubkey");
98  }
99  bool operator()(const CExchangeKeyID &id) const
100  {
101  return (exp_addrType == "exchangepubkey");
102  }
103  bool operator()(const CScriptID &id) const
104  {
105  return (exp_addrType == "script");
106  }
107  bool operator()(const CNoDestination &no) const
108  {
109  return (exp_addrType == "none");
110  }
111 };
112 
113 // Visitor to check address payload
114 class TestPayloadVisitor : public boost::static_visitor<bool>
115 {
116 private:
117  std::vector<unsigned char> exp_payload;
118 public:
119  explicit TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
120  bool operator()(const CKeyID &id) const
121  {
122  uint160 exp_key(exp_payload);
123  return exp_key == id;
124  }
125  bool operator()(const CScriptID &id) const
126  {
127  uint160 exp_key(exp_payload);
128  return exp_key == id;
129  }
130  bool operator()(const CNoDestination &no) const
131  {
132  return exp_payload.size() == 0;
133  }
134 };
135 
136 // Goal: check that parsed keys match test payload
137 BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
138 {
139  UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
140  std::vector<unsigned char> result;
141  CKey privkey;
142  CTxDestination destination;
144 
145  for (unsigned int idx = 0; idx < tests.size(); idx++) {
146  UniValue test = tests[idx];
147  std::string strTest = test.write();
148  if (test.size() < 3) // Allow for extra stuff (useful for comments)
149  {
150  BOOST_ERROR("Bad test: " << strTest);
151  continue;
152  }
153  std::string exp_base58string = test[0].get_str();
154  std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
155  const UniValue &metadata = test[2].get_obj();
156  bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
157  bool isTestnet = find_value(metadata, "isTestnet").get_bool();
158  if (isTestnet)
160  else
162  if(isPrivkey) {
163  bool isCompressed = find_value(metadata, "isCompressed").get_bool();
164  // Must be valid private key
165  privkey = KeyIO::DecodeSecret(exp_base58string);
166  BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
167  BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
168  BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
169 
170  // Private key must be invalid public key
171  destination = DecodeDestination(exp_base58string);
172  BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
173  } else {
174  std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
175  // Must be valid public key
176  destination = DecodeDestination(exp_base58string);
177  BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
178  BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
179  BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
180 
181  // Public key must be invalid private key
182  privkey = KeyIO::DecodeSecret(exp_base58string);
183  BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest);
184  }
185  }
186 }
187 
188 // Goal: check that generated keys match test vectors
189 BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
190 {
191  UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
192  std::vector<unsigned char> result;
193 
194  for (unsigned int idx = 0; idx < tests.size(); idx++) {
195  UniValue test = tests[idx];
196  std::string strTest = test.write();
197  if (test.size() < 3) // Allow for extra stuff (useful for comments)
198  {
199  BOOST_ERROR("Bad test: " << strTest);
200  continue;
201  }
202  std::string exp_base58string = test[0].get_str();
203  std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
204  const UniValue &metadata = test[2].get_obj();
205  bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
206  bool isTestnet = find_value(metadata, "isTestnet").get_bool();
207  if (isTestnet)
209  else
211  if(isPrivkey)
212  {
213  bool isCompressed = find_value(metadata, "isCompressed").get_bool();
214  CKey key;
215  key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
216  assert(key.IsValid());
217  BOOST_CHECK_MESSAGE(KeyIO::EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest);
218  }
219  else
220  {
221  std::string exp_addrType = find_value(metadata, "addrType").get_str();
222  CTxDestination dest;
223  if(exp_addrType == "pubkey")
224  {
225  dest = CKeyID(uint160(exp_payload));
226  }
227  else if(exp_addrType == "script")
228  {
229  dest = CScriptID(uint160(exp_payload));
230  }
231  else if(exp_addrType == "none")
232  {
233  dest = CNoDestination();
234  }
235  else
236  {
237  BOOST_ERROR("Bad addrtype: " << strTest);
238  continue;
239  }
240  std::string address = EncodeDestination(dest);
241  BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
242  }
243  }
244 
246 }
247 
248 // Goal: check that base58 parsing code is robust against a variety of corrupted data
249 BOOST_AUTO_TEST_CASE(base58_keys_invalid)
250 {
251  UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
252  std::vector<unsigned char> result;
253  CKey privkey;
254  CTxDestination destination;
255 
256  for (unsigned int idx = 0; idx < tests.size(); idx++) {
257  UniValue test = tests[idx];
258  std::string strTest = test.write();
259  if (test.size() < 1) // Allow for extra stuff (useful for comments)
260  {
261  BOOST_ERROR("Bad test: " << strTest);
262  continue;
263  }
264  std::string exp_base58string = test[0].get_str();
265 
266  // must be invalid as public and as private key
267  destination = DecodeDestination(exp_base58string);
268  BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
269  privkey = KeyIO::DecodeSecret(exp_base58string);
270  BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey:" + strTest);
271  }
272 }
273 
274 BOOST_AUTO_TEST_CASE(base58_random_encode_decode)
275 {
276  for (int n = 0; n < 1000; ++n) {
277  unsigned int len = 1 + InsecureRandBits(8);
278  unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0;
279  auto data = Cat(std::vector<unsigned char>(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes));
280  auto encoded = EncodeBase58Check(data);
281  std::vector<unsigned char> decoded;
282  auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len));
283  BOOST_CHECK(!ok_too_small);
284  auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len));
285  BOOST_CHECK(ok);
286  BOOST_CHECK(data == decoded);
287  }
288 }
289 
291 
std::string EncodeBase58(const unsigned char *pbegin, const unsigned char *pend)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:73
bool DecodeBase58Check(const char *psz, std::vector< unsigned char > &vchRet, int max_ret_len)
Decode a base58-encoded string (psz) that includes a checksum into a byte vector (vchRet),...
Definition: base58.cpp:135
std::string EncodeBase58Check(const std::vector< unsigned char > &vchIn)
Encode a byte vector into a base58-encoded string, including checksum.
Definition: base58.cpp:126
bool DecodeBase58(const char *psz, std::vector< unsigned char > &vch, int max_ret_len)
Decode a base58-encoded string (psz) into a byte vector (vchRet).
Definition: base58.cpp:23
UniValue read_json(const std::string &jsondata)
BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given chain name.
static const std::string TESTNET
static const std::string MAIN
Chain name strings.
A reference to a CKey: the Hash160 of its serialized public key, special case for exchange key.
Definition: pubkey.h:30
An encapsulated private key.
Definition: key.h:30
const unsigned char * end() const
Definition: key.h:92
unsigned int size() const
Simple read-only vector-like interface.
Definition: key.h:90
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:95
bool IsCompressed() const
Check whether the public key corresponding to this private key is (to be) compressed.
Definition: key.h:98
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:76
const unsigned char * begin() const
Definition: key.h:91
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:21
A reference to a CScript: the Hash160 of its serialization (see script.h)
Definition: standard.h:24
std::vector< unsigned char > randbytes(size_t len)
Generate random bytes.
Definition: random.cpp:632
std::string exp_addrType
bool operator()(const CScriptID &id) const
bool operator()(const CKeyID &id) const
bool operator()(const CExchangeKeyID &id) const
TestAddrTypeVisitor(const std::string &exp_addrType)
bool operator()(const CNoDestination &no) const
std::vector< unsigned char > exp_payload
TestPayloadVisitor(std::vector< unsigned char > &exp_payload)
bool operator()(const CKeyID &id) const
bool operator()(const CScriptID &id) const
bool operator()(const CNoDestination &no) const
const std::string & get_str() const
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
const UniValue & get_obj() const
size_t size() const
Definition: univalue.h:68
bool get_bool() const
160-bit opaque blob.
Definition: uint256.h:127
BOOST_AUTO_TEST_SUITE_END()
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:145
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:127
bool IsValidDestination(const CWDestination &address)
std::string EncodeDestination(const CWDestination &address, const CChainParams::Base58Type addrType)
CWDestination DecodeDestination(const std::string &strAddress)
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK(expr)
Definition: object.cpp:17
boost::variant< CNoDestination, CKeyID, CScriptID, CExchangeKeyID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:72
Basic testing setup.
Definition: test_pivx.h:51
FastRandomContext g_insecure_rand_ctx
Definition: test_pivx.cpp:35
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:234
std::vector< unsigned char > ParseHex(const char *psz)
V Cat(V v1, V &&v2)
Concatenate two vectors, moving elements.
Definition: vector.h:31