/* * This file is part of the Flowee project * Copyright (C) 2011-2015 The Bitcoin Core developers * Copyright (C) 2022 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "base58_tests.h" #include #include #include #include #include #include #include // Visitor to check address type class TestAddrTypeVisitor : public boost::static_visitor { private: std::string exp_addrType; public: TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { } bool operator()(const KeyId &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"); } }; void Base58Tests::base58KeysValidParse() { QFile in(":/base58_keys_valid.json"); QVERIFY(in.open(QIODevice::ReadOnly)); auto testDoc = QJsonDocument::fromJson(in.readAll()); QVERIFY(testDoc.isArray()); auto tests = testDoc.array(); CBitcoinSecret secret; CBitcoinAddress addr; SelectParams(CBaseChainParams::MAIN); for (auto row = tests.begin(); row != tests.end(); ++row) { auto test = row->toArray(); if (test.size() < 3) { // Allow for extra stuff (useful for comments) QFAIL("Bad test"); continue; } std::string exp_base58string = test[0].toString().toStdString(); auto exp_payload = ParseHex(test[1].toString().toStdString()); auto metadata = test[2].toObject(); bool isPrivkey = metadata.value("isPrivkey").toBool(); bool isTestnet = metadata.value("isTestnet").toBool(); if (isTestnet) SelectParams(CBaseChainParams::TESTNET); else SelectParams(CBaseChainParams::MAIN); if (isPrivkey) { PrivateKey key; bool isCompressed = metadata.value("isCompressed").toBool(); // Must be valid private key // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! QVERIFY2(secret.SetString(exp_base58string), "!SetString"); QVERIFY2(secret.IsValid(), "!IsValid"); PrivateKey privkey = secret.GetKey(); QVERIFY2(privkey.isCompressed() == isCompressed, "compressed mismatch"); QVERIFY2(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch"); // Private key must be invalid public key addr.SetString(exp_base58string); QVERIFY2(!addr.IsValid(), "IsValid privkey as pubkey"); } else { std::string exp_addrType = metadata.value("addrType").toString().toStdString(); // "script" or "pubkey" // Must be valid public key QVERIFY2(addr.SetString(exp_base58string), "SetString"); QVERIFY2(addr.IsValid(), "!IsValid"); QVERIFY2(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch"); CTxDestination dest = addr.Get(); QVERIFY2(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch"); // Public key must be invalid private key secret.SetString(exp_base58string); QVERIFY2(!secret.IsValid(), "IsValid pubkey as privkey"); } } } // Goal: check that generated keys match test vectors void Base58Tests::base58KeysValidGen() { QFile in(":/base58_keys_valid.json"); QVERIFY(in.open(QIODevice::ReadOnly)); auto testDoc = QJsonDocument::fromJson(in.readAll()); QVERIFY(testDoc.isArray()); auto tests = testDoc.array(); for (unsigned int idx = 0; idx < tests.size(); idx++) { auto test = tests[idx].toArray(); if (test.size() < 3) { // Allow for extra stuff (useful for comments) QFAIL("Bad test"); continue; } std::string exp_base58string = test[0].toString().toStdString(); auto exp_payload = ParseHex(test[1].toString().toStdString()); auto metadata = test[2].toObject(); bool isPrivkey = metadata.value("isPrivkey").toBool(); bool isTestnet = metadata.value("isTestnet").toBool(); if (isTestnet) SelectParams(CBaseChainParams::TESTNET); else SelectParams(CBaseChainParams::MAIN); if (isPrivkey) { bool isCompressed = metadata.value("isCompressed").toBool(); PrivateKey key; key.set(exp_payload.begin(), exp_payload.end(), isCompressed); QVERIFY(key.isValid()); CBitcoinSecret secret; secret.SetKey(key); QVERIFY2(secret.ToString() == exp_base58string, "result mismatch"); } else { std::string exp_addrType = metadata.value("addrType").toString().toStdString(); // "script" or "pubkey" CTxDestination dest; if(exp_addrType == "pubkey") { dest = KeyId(uint160(exp_payload)); } else if(exp_addrType == "script") { dest = CScriptID(uint160(exp_payload)); } else if(exp_addrType == "none") { dest = CNoDestination(); } else { QFAIL("Bad addrtype"); continue; } CBitcoinAddress addrOut; QVERIFY2(addrOut.Set(dest), "encode dest"); QVERIFY2(addrOut.ToString() == exp_base58string, "mismatch"); } } // Visiting a CNoDestination must fail CBitcoinAddress dummyAddr; CTxDestination nodest = CNoDestination(); QVERIFY(!dummyAddr.Set(nodest)); SelectParams(CBaseChainParams::MAIN); } // Goal: check that base58 parsing code is robust against a variety of corrupted data void Base58Tests::base58KeysInvalid() { QFile in(":/base58_keys_invalid.json"); QVERIFY(in.open(QIODevice::ReadOnly)); auto testDoc = QJsonDocument::fromJson(in.readAll()); QVERIFY(testDoc.isArray()); auto tests = testDoc.array(); CBitcoinSecret secret; CBitcoinAddress addr; for (unsigned int idx = 0; idx < tests.size(); idx++) { auto test = tests[idx].toArray(); if (test.size() < 1) { // Allow for extra stuff (useful for comments) QFAIL("Bad test"); continue; } std::string exp_base58string = test[0].toString().toStdString(); // must be invalid as public and as private key addr.SetString(exp_base58string); QVERIFY2(!addr.IsValid(), "IsValid pubkey"); secret.SetString(exp_base58string); QVERIFY2(!secret.IsValid(), "IsValid privkey"); } }