#include #include "json/json_compat.h" #include "base58.h" #include "util.h" using namespace json_spirit; extern Array read_json(const std::string& filename); BOOST_AUTO_TEST_SUITE(base58_tests) // Goal: test low-level base58 encoding functionality BOOST_AUTO_TEST_CASE(base58_EncodeBase58) { Array tests = read_json("base58_encode_decode.json"); for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); if (test.size() < 2) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); continue; } std::vector sourcedata = ParseHex(test[0].get_str()); std::string base58string = test[1].get_str(); BOOST_CHECK_MESSAGE( EncodeBase58(sourcedata) == base58string, strTest); } } // Goal: test low-level base58 decoding functionality BOOST_AUTO_TEST_CASE(base58_DecodeBase58) { Array tests = read_json("base58_encode_decode.json"); std::vector result; for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); if (test.size() < 2) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); continue; } std::vector expected = ParseHex(test[0].get_str()); std::string base58string = test[1].get_str(); BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest); BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest); } BOOST_CHECK(!DecodeBase58("invalid", result)); } // Visitor to check address type class TestAddrTypeVisitor { private: std::string exp_addrType; public: TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { } bool operator()(const CKeyID &id) const { return (exp_addrType == "pubkey"); } bool operator()(const CScriptID &id) const { return (exp_addrType == "script"); } bool operator()(const CNoDestination &no) const { return (exp_addrType == "none"); } }; // Visitor to check address payload class TestPayloadVisitor { private: std::vector exp_payload; public: TestPayloadVisitor(std::vector &exp_payload) : exp_payload(exp_payload) { } bool operator()(const CKeyID &id) const { uint160 exp_key(exp_payload); return exp_key == id; } bool operator()(const CScriptID &id) const { uint160 exp_key(exp_payload); return exp_key == id; } bool operator()(const CNoDestination &no) const { return exp_payload.size() == 0; } }; // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { Array tests = read_json("base58_keys_valid.json"); std::vector result; CTrianglesSecret secret; CTrianglesAddress addr; // Save global state bool fTestNet_stored = fTestNet; for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); if (test.size() < 3) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); continue; } std::string exp_base58string = test[0].get_str(); std::vector exp_payload = ParseHex(test[1].get_str()); const Object &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); fTestNet = isTestnet; // Override testnet flag if(isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key // Note: CTrianglesSecret::SetString tests isValid, whereas CTrianglesAddress does not! BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); bool fCompressedOut = false; CSecret privkey = secret.GetSecret(fCompressedOut); BOOST_CHECK_MESSAGE(fCompressedOut == isCompressed, "compressed mismatch:" + strTest); BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); // Private key must be invalid public key addr.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); } else { std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" // Must be valid public key BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); CTxDestination dest = addr.Get(); BOOST_CHECK_MESSAGE(std::visit(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); // Public key must be invalid private key secret.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); } } // Restore global state fTestNet = fTestNet_stored; } // Goal: check that generated keys match test vectors BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) { Array tests = read_json("base58_keys_valid.json"); std::vector result; // Save global state bool fTestNet_stored = fTestNet; for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); if (test.size() < 3) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); continue; } std::string exp_base58string = test[0].get_str(); std::vector exp_payload = ParseHex(test[1].get_str()); const Object &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isTestnet = find_value(metadata, "isTestnet").get_bool(); fTestNet = isTestnet; // Override testnet flag if(isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CTrianglesSecret secret; secret.SetSecret(CSecret(exp_payload.begin(), exp_payload.end()), isCompressed); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); } else { std::string exp_addrType = find_value(metadata, "addrType").get_str(); CTxDestination dest; if(exp_addrType == "pubkey") { dest = CKeyID(uint160(exp_payload)); } else if(exp_addrType == "script") { dest = CScriptID(uint160(exp_payload)); } else if(exp_addrType == "none") { dest = CNoDestination(); } else { BOOST_ERROR("Bad addrtype: " << strTest); continue; } CTrianglesAddress addrOut; BOOST_CHECK_MESSAGE(std::visit(CTrianglesAddressVisitor(&addrOut), dest), "encode dest: " + strTest); BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); } } // Visiting a CNoDestination must fail CTrianglesAddress dummyAddr; CTxDestination nodest = CNoDestination(); BOOST_CHECK(!std::visit(CTrianglesAddressVisitor(&dummyAddr), nodest)); // Restore global state fTestNet = fTestNet_stored; } // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { Array tests = read_json("base58_keys_invalid.json"); // Negative testcases std::vector result; CTrianglesSecret secret; CTrianglesAddress addr; for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); if (test.size() < 1) // Allow for extra stuff (useful for comments) { BOOST_ERROR("Bad test: " << strTest); continue; } std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key addr.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); secret.SetString(exp_base58string); BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); } } BOOST_AUTO_TEST_SUITE_END()