<commit_before>// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "base58.h"
#include "rpcserver.h"
#include "init.h"
#include "main.h"
#include "sync.h"
#include "wallet.h"

#include <fstream>
#include <stdint.h>

#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include "json/json_spirit_value.h"

using namespace json_spirit;
using namespace std;

void EnsureWalletIsUnlocked();

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%"PRId64"T%H:%M:%SZ", nTime);
}

int64_t static DecodeDumpTime(const std::string &str) {
    static boost::posix_time::time_input_facet facet("%Y-%m-%dT%H:%M:%SZ");
    static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
    const std::locale loc(std::locale::classic(), &facet);
    std::istringstream iss(str);
    iss.imbue(loc);
    boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
    iss >> ptime;
    if (ptime.is_not_a_date_time())
        return 0;
    return (ptime - epoch).total_seconds();
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    BOOST_FOREACH(unsigned char c, str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

Value importprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
            "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
            "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
            "\nArguments:\n"
            "1. \"bitcoinprivkey\"   (string, required) The private key (see dumpprivkey)\n"
            "2. \"label\"            (string, optional) an optional label\n"
            "3. rescan               (boolean, optional, default=true) Rescan the wallet for transactions\n"
            "\nExamples:\n"
            "\nDump a private key\n"
            + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
            "\nImport the private key\n"
            + HelpExampleCli("importprivkey", "\"mykey\"") +
            "\nImport using a label\n"
            + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
            "\nAs a json rpc call\n"
            + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
        );

    EnsureWalletIsUnlocked();

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

    // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    bool fGood = vchSecret.SetString(strSecret);

    if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");

    CKey key = vchSecret.GetKey();
    CPubKey pubkey = key.GetPubKey();
    CKeyID vchAddress = pubkey.GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();
        pwalletMain->SetAddressBook(vchAddress, strLabel, "receive");

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return Value::null;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKeyPubKey(key, pubkey))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'

        if (fRescan) {
            pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return Value::null;
}

Value importwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet \"filename\"\n"
            "\nImports keys from a wallet dump file (see dumpwallet).\n"
            "\nArguments:\n"
            "1. \"filename\"    (string, required) The wallet file\n"
            "\nExamples:\n"
            "\nDump the wallet\n"
            + HelpExampleCli("dumpwallet", "\"test\"") +
            "\nImport the wallet\n"
            + HelpExampleCli("importwallet", "\"test\"") +
            "\nImport using the json rpc call\n"
            + HelpExampleRpc("importwallet", "\"test\"")
        );

    EnsureWalletIsUnlocked();

    ifstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    int64_t nTimeBegin = chainActive.Tip()->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;
        CKey key = vchSecret.GetKey();
        CPubKey pubkey = key.GetPubKey();
        CKeyID keyid = pubkey.GetID();
        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBook(keyid, strLabel, "receive");
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = chainActive.Tip();
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return Value::null;
}

Value dumpprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey \"bitcoinaddress\"\n"
            "\nReveals the private key corresponding to 'bitcoinaddress'.\n"
            "Then the importprivkey can be used with this output\n"
            "\nArguments:\n"
            "1. \"bitcoinaddress\"   (string, required) The bitcoin address for the private key\n"
            "\nResult:\n"
            "\"key\"                (string) The private key\n"
            "\nExamples:\n"
            + HelpExampleCli("dumpprivkey", "\"myaddress\"")
            + HelpExampleCli("importprivkey", "\"mykey\"")
            + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
        );

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CKey vchSecret;
    if (!pwalletMain->GetKey(keyID, vchSecret))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret).ToString();
}

Value dumpwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet \"filename\"\n"
            "\nDumps all wallet keys in a human-readable format.\n"
            "\nArguments:\n"
            "1. \"filename\"    (string, required) The filename\n"
            "\nExamples:\n"
            + HelpExampleCli("dumpwallet", "\"test\"")
            + HelpExampleRpc("dumpwallet", "\"test\"")
        );

    EnsureWalletIsUnlocked();

    ofstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;
    std::set<CKeyID> setKeyPool;
    pwalletMain->GetKeyBirthTimes(mapKeyBirth);
    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();
        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
            } else if (setKeyPool.count(keyid)) {
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            } else {
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return Value::null;
}
<commit_msg>Add check for valid keys in `importprivkey`<commit_after>// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "base58.h"
#include "rpcserver.h"
#include "init.h"
#include "main.h"
#include "sync.h"
#include "wallet.h"

#include <fstream>
#include <stdint.h>

#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include "json/json_spirit_value.h"

using namespace json_spirit;
using namespace std;

void EnsureWalletIsUnlocked();

std::string static EncodeDumpTime(int64_t nTime) {
    return DateTimeStrFormat("%Y-%m-%"PRId64"T%H:%M:%SZ", nTime);
}

int64_t static DecodeDumpTime(const std::string &str) {
    static boost::posix_time::time_input_facet facet("%Y-%m-%dT%H:%M:%SZ");
    static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
    const std::locale loc(std::locale::classic(), &facet);
    std::istringstream iss(str);
    iss.imbue(loc);
    boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
    iss >> ptime;
    if (ptime.is_not_a_date_time())
        return 0;
    return (ptime - epoch).total_seconds();
}

std::string static EncodeDumpString(const std::string &str) {
    std::stringstream ret;
    BOOST_FOREACH(unsigned char c, str) {
        if (c <= 32 || c >= 128 || c == '%') {
            ret << '%' << HexStr(&c, &c + 1);
        } else {
            ret << c;
        }
    }
    return ret.str();
}

std::string DecodeDumpString(const std::string &str) {
    std::stringstream ret;
    for (unsigned int pos = 0; pos < str.length(); pos++) {
        unsigned char c = str[pos];
        if (c == '%' && pos+2 < str.length()) {
            c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
                ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
            pos += 2;
        }
        ret << c;
    }
    return ret.str();
}

Value importprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 3)
        throw runtime_error(
            "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
            "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
            "\nArguments:\n"
            "1. \"bitcoinprivkey\"   (string, required) The private key (see dumpprivkey)\n"
            "2. \"label\"            (string, optional) an optional label\n"
            "3. rescan               (boolean, optional, default=true) Rescan the wallet for transactions\n"
            "\nExamples:\n"
            "\nDump a private key\n"
            + HelpExampleCli("dumpprivkey", "\"myaddress\"") +
            "\nImport the private key\n"
            + HelpExampleCli("importprivkey", "\"mykey\"") +
            "\nImport using a label\n"
            + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
            "\nAs a json rpc call\n"
            + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
        );

    EnsureWalletIsUnlocked();

    string strSecret = params[0].get_str();
    string strLabel = "";
    if (params.size() > 1)
        strLabel = params[1].get_str();

    // Whether to perform rescan after import
    bool fRescan = true;
    if (params.size() > 2)
        fRescan = params[2].get_bool();

    CBitcoinSecret vchSecret;
    bool fGood = vchSecret.SetString(strSecret);

    if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");

    CKey key = vchSecret.GetKey();
    if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");

    CPubKey pubkey = key.GetPubKey();
    CKeyID vchAddress = pubkey.GetID();
    {
        LOCK2(cs_main, pwalletMain->cs_wallet);

        pwalletMain->MarkDirty();
        pwalletMain->SetAddressBook(vchAddress, strLabel, "receive");

        // Don't throw error in case a key is already there
        if (pwalletMain->HaveKey(vchAddress))
            return Value::null;

        pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;

        if (!pwalletMain->AddKeyPubKey(key, pubkey))
            throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");

        // whenever a key is imported, we need to scan the whole chain
        pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'

        if (fRescan) {
            pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
            pwalletMain->ReacceptWalletTransactions();
        }
    }

    return Value::null;
}

Value importwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet \"filename\"\n"
            "\nImports keys from a wallet dump file (see dumpwallet).\n"
            "\nArguments:\n"
            "1. \"filename\"    (string, required) The wallet file\n"
            "\nExamples:\n"
            "\nDump the wallet\n"
            + HelpExampleCli("dumpwallet", "\"test\"") +
            "\nImport the wallet\n"
            + HelpExampleCli("importwallet", "\"test\"") +
            "\nImport using the json rpc call\n"
            + HelpExampleRpc("importwallet", "\"test\"")
        );

    EnsureWalletIsUnlocked();

    ifstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    int64_t nTimeBegin = chainActive.Tip()->nTime;

    bool fGood = true;

    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#')
            continue;

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" "));
        if (vstr.size() < 2)
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0]))
            continue;
        CKey key = vchSecret.GetKey();
        CPubKey pubkey = key.GetPubKey();
        CKeyID keyid = pubkey.GetID();
        if (pwalletMain->HaveKey(keyid)) {
            LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]);
        std::string strLabel;
        bool fLabel = true;
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
            if (boost::algorithm::starts_with(vstr[nStr], "#"))
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6));
                fLabel = true;
            }
        }
        LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString());
        if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime;
        if (fLabel)
            pwalletMain->SetAddressBook(keyid, strLabel, "receive");
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close();

    CBlockIndex *pindex = chainActive.Tip();
    while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200)
        pindex = pindex->pprev;

    LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex);
    pwalletMain->ReacceptWalletTransactions();
    pwalletMain->MarkDirty();

    if (!fGood)
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return Value::null;
}

Value dumpprivkey(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpprivkey \"bitcoinaddress\"\n"
            "\nReveals the private key corresponding to 'bitcoinaddress'.\n"
            "Then the importprivkey can be used with this output\n"
            "\nArguments:\n"
            "1. \"bitcoinaddress\"   (string, required) The bitcoin address for the private key\n"
            "\nResult:\n"
            "\"key\"                (string) The private key\n"
            "\nExamples:\n"
            + HelpExampleCli("dumpprivkey", "\"myaddress\"")
            + HelpExampleCli("importprivkey", "\"mykey\"")
            + HelpExampleRpc("dumpprivkey", "\"myaddress\"")
        );

    EnsureWalletIsUnlocked();

    string strAddress = params[0].get_str();
    CBitcoinAddress address;
    if (!address.SetString(strAddress))
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
    CKeyID keyID;
    if (!address.GetKeyID(keyID))
        throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    CKey vchSecret;
    if (!pwalletMain->GetKey(keyID, vchSecret))
        throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    return CBitcoinSecret(vchSecret).ToString();
}

Value dumpwallet(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "dumpwallet \"filename\"\n"
            "\nDumps all wallet keys in a human-readable format.\n"
            "\nArguments:\n"
            "1. \"filename\"    (string, required) The filename\n"
            "\nExamples:\n"
            + HelpExampleCli("dumpwallet", "\"test\"")
            + HelpExampleRpc("dumpwallet", "\"test\"")
        );

    EnsureWalletIsUnlocked();

    ofstream file;
    file.open(params[0].get_str().c_str());
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    std::map<CKeyID, int64_t> mapKeyBirth;
    std::set<CKeyID> setKeyPool;
    pwalletMain->GetKeyBirthTimes(mapKeyBirth);
    pwalletMain->GetAllReserveKeys(setKeyPool);

    // sort time/key pairs
    std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
    for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) {
        vKeyBirth.push_back(std::make_pair(it->second, it->first));
    }
    mapKeyBirth.clear();
    std::sort(vKeyBirth.begin(), vKeyBirth.end());

    // produce output
    file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
    file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
    file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
    file << strprintf("#   mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime));
    file << "\n";
    for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
        const CKeyID &keyid = it->second;
        std::string strTime = EncodeDumpTime(it->first);
        std::string strAddr = CBitcoinAddress(keyid).ToString();
        CKey key;
        if (pwalletMain->GetKey(keyid, key)) {
            if (pwalletMain->mapAddressBook.count(keyid)) {
                file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
            } else if (setKeyPool.count(keyid)) {
                file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            } else {
                file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
            }
        }
    }
    file << "\n";
    file << "# End of dump\n";
    file.close();
    return Value::null;
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "test/test_pivx.h"
#include "primitives/transaction.h"
#include "evo/providertx.h"
#include "evo/specialtx.h"
#include "messagesigner.h"
#include "netbase.h"

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(evo_specialtx_tests, TestingSetup)

static void RandomScript(CScript &script)
{
    static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
    script = CScript();
    int ops = (InsecureRandRange(10));
    for (int i=0; i<ops; i++)
        script << oplist[InsecureRandRange(sizeof(oplist)/sizeof(oplist[0]))];
}

static CKey GetRandomKey()
{
    CKey key;
    key.MakeNewKey(true);
    return key;
}

static CKeyID GetRandomKeyID()
{
    return GetRandomKey().GetPubKey().GetID();
}

static ProRegPL GetRandomProRegPayload()
{
    ProRegPL pl;
    pl.collateralOutpoint.hash = GetRandHash();
    pl.collateralOutpoint.n = InsecureRandBits(2);
    BOOST_CHECK(Lookup("127.0.0.1:51472", pl.addr, Params().GetDefaultPort(), false));
    pl.keyIDOwner = GetRandomKeyID();
    pl.keyIDOperator = GetRandomKeyID();
    pl.keyIDVoting = GetRandomKeyID();
    RandomScript(pl.scriptPayout);
    pl.nOperatorReward = InsecureRandRange(10000);
    RandomScript(pl.scriptOperatorPayout);
    pl.inputsHash = GetRandHash();
    pl.vchSig = InsecureRandBytes(63);
    return pl;
}

BOOST_AUTO_TEST_CASE(special_tx_validation_test)
{
    CMutableTransaction mtx;
    CValidationState state;

    // v1 can only be Type=0
    mtx.nType = CTransaction::TxType::PROREG;
    mtx.nVersion = CTransaction::TxVersion::LEGACY;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-version");

    // version >= Sapling, type = 0, payload != null.
    mtx.nType = CTransaction::TxType::NORMAL;
    mtx.extraPayload = std::vector<uint8_t>(10, 1);
    mtx.nVersion = CTransaction::TxVersion::SAPLING;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-payload");

    // version >= Sapling, type = 0, payload == null --> pass
    mtx.extraPayload = nullopt;
    BOOST_CHECK(CheckSpecialTxNoContext(CTransaction(mtx), state));

    // nVersion>=2 and nType!=0 without extrapayload
    mtx.nType = CTransaction::TxType::PROREG;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK(state.GetRejectReason().find("without extra payload"));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-empty");

    // Size limits
    mtx.extraPayload = std::vector<uint8_t>(MAX_SPECIALTX_EXTRAPAYLOAD + 1, 1);
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-oversize");

    // Remove one element, so now it passes the size check
    mtx.extraPayload->pop_back();
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-payload");
}

BOOST_AUTO_TEST_CASE(providertx_setpayload_test)
{
    const ProRegPL& pl = GetRandomProRegPayload();

    CMutableTransaction mtx;
    SetTxPayload(mtx, pl);
    ProRegPL pl2;
    BOOST_CHECK(GetTxPayload(mtx, pl2));
    BOOST_CHECK(pl.collateralOutpoint == pl2.collateralOutpoint);
    BOOST_CHECK(pl.addr  == pl2.addr);
    BOOST_CHECK(pl.keyIDOwner == pl2.keyIDOwner);
    BOOST_CHECK(pl.keyIDOperator == pl2.keyIDOperator);
    BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting);
    BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout);
    BOOST_CHECK(pl.nOperatorReward  == pl2.nOperatorReward);
    BOOST_CHECK(pl.scriptOperatorPayout == pl2.scriptOperatorPayout);
    BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
    BOOST_CHECK(pl.vchSig == pl2.vchSig);
}

BOOST_AUTO_TEST_CASE(providertx_checkstringsig_test)
{
    ProRegPL pl = GetRandomProRegPayload();
    pl.vchSig.clear();
    const CKey& key = GetRandomKey();
    BOOST_CHECK(CMessageSigner::SignMessage(pl.MakeSignString(), pl.vchSig, key));

    std::string strError;
    const CKeyID& keyID = key.GetPubKey().GetID();
    BOOST_CHECK(CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
    // Change owner address or script payout
    pl.keyIDOwner = GetRandomKeyID();
    BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
    RandomScript(pl.scriptPayout);
    BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>[Tests] Check for protx inputs hash in evo_specialtx_tests<commit_after>// Copyright (c) 2021 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include "test/test_pivx.h"
#include "primitives/transaction.h"
#include "evo/providertx.h"
#include "evo/specialtx.h"
#include "messagesigner.h"
#include "netbase.h"

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(evo_specialtx_tests, TestingSetup)

static CKey GetRandomKey()
{
    CKey key;
    key.MakeNewKey(true);
    return key;
}

static CKeyID GetRandomKeyID()
{
    return GetRandomKey().GetPubKey().GetID();
}

static CScript GetRandomScript()
{
    return GetScriptForDestination(GetRandomKeyID());
}

static ProRegPL GetRandomProRegPayload()
{
    ProRegPL pl;
    pl.collateralOutpoint.hash = GetRandHash();
    pl.collateralOutpoint.n = InsecureRandBits(2);
    BOOST_CHECK(Lookup("57.12.210.11:51472", pl.addr, Params().GetDefaultPort(), false));
    pl.keyIDOwner = GetRandomKeyID();
    pl.keyIDOperator = GetRandomKeyID();
    pl.keyIDVoting = GetRandomKeyID();
    pl.scriptPayout = GetRandomScript();
    pl.nOperatorReward = InsecureRandRange(10000);
    pl.scriptOperatorPayout = GetRandomScript();
    pl.inputsHash = GetRandHash();
    pl.vchSig = InsecureRandBytes(63);
    return pl;
}

BOOST_AUTO_TEST_CASE(special_tx_validation_test)
{
    CMutableTransaction mtx;
    CValidationState state;

    // v1 can only be Type=0
    mtx.nType = CTransaction::TxType::PROREG;
    mtx.nVersion = CTransaction::TxVersion::LEGACY;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-version");

    // version >= Sapling, type = 0, payload != null.
    mtx.nType = CTransaction::TxType::NORMAL;
    mtx.extraPayload = std::vector<uint8_t>(10, 1);
    mtx.nVersion = CTransaction::TxVersion::SAPLING;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-payload");

    // version >= Sapling, type = 0, payload == null --> pass
    mtx.extraPayload = nullopt;
    BOOST_CHECK(CheckSpecialTxNoContext(CTransaction(mtx), state));

    // nVersion>=2 and nType!=0 without extrapayload
    mtx.nType = CTransaction::TxType::PROREG;
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK(state.GetRejectReason().find("without extra payload"));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-empty");

    // Size limits
    mtx.extraPayload = std::vector<uint8_t>(MAX_SPECIALTX_EXTRAPAYLOAD + 1, 1);
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-oversize");

    // Remove one element, so now it passes the size check
    mtx.extraPayload->pop_back();
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-payload");

    // valid payload but invalid inputs hash
    mtx.extraPayload->clear();
    ProRegPL pl = GetRandomProRegPayload();
    SetTxPayload(mtx, pl);
    BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
    BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-inputs-hash");

    // all good.
    mtx.vin.emplace_back(GetRandHash(), 0);
    mtx.extraPayload->clear();
    pl.inputsHash = CalcTxInputsHash(CTransaction(mtx));
    SetTxPayload(mtx, pl);
    BOOST_CHECK(CheckSpecialTxNoContext(CTransaction(mtx), state));
}

BOOST_AUTO_TEST_CASE(providertx_setpayload_test)
{
    const ProRegPL& pl = GetRandomProRegPayload();

    CMutableTransaction mtx;
    SetTxPayload(mtx, pl);
    ProRegPL pl2;
    BOOST_CHECK(GetTxPayload(mtx, pl2));
    BOOST_CHECK(pl.collateralOutpoint == pl2.collateralOutpoint);
    BOOST_CHECK(pl.addr  == pl2.addr);
    BOOST_CHECK(pl.keyIDOwner == pl2.keyIDOwner);
    BOOST_CHECK(pl.keyIDOperator == pl2.keyIDOperator);
    BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting);
    BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout);
    BOOST_CHECK(pl.nOperatorReward  == pl2.nOperatorReward);
    BOOST_CHECK(pl.scriptOperatorPayout == pl2.scriptOperatorPayout);
    BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
    BOOST_CHECK(pl.vchSig == pl2.vchSig);
}

BOOST_AUTO_TEST_CASE(providertx_checkstringsig_test)
{
    ProRegPL pl = GetRandomProRegPayload();
    pl.vchSig.clear();
    const CKey& key = GetRandomKey();
    BOOST_CHECK(CMessageSigner::SignMessage(pl.MakeSignString(), pl.vchSig, key));

    std::string strError;
    const CKeyID& keyID = key.GetPubKey().GetID();
    BOOST_CHECK(CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
    // Change owner address or script payout
    pl.keyIDOwner = GetRandomKeyID();
    BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
    pl.scriptPayout = GetRandomScript();
    BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------//
//!
//! \file   tstLinearAlgebraAlgorithms.cpp
//! \author Alex Robinson
//! \brief  Linear algebra algorithm unit tests.
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>

// Trilinos Includes
#include <Teuchos_UnitTestHarness.hpp>
#include <Teuchos_ScalarTraits.hpp>

// FRENSIE Includes
#include "Geometry_LinearAlgebraAlgorithms.hpp"
#include "Geometry_VectorHelpers.hpp"
#include "Geometry_MatrixHelpers.hpp"
#include "Utility_UnitTestHarnessExtensions.hpp"

//---------------------------------------------------------------------------//
// Instantiation Macros.
//---------------------------------------------------------------------------//
#define UNIT_TEST_INSTANTIATION( type, name ) \
  TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( type, name, float ) \
  TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( type, name, double )

//---------------------------------------------------------------------------//
// Tests.
//---------------------------------------------------------------------------//
// Check that the cross product of two vectors can be calculated
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeCrossProduct,
				   ScalarType )
{
  // cross product of general vectors
  Geometry::Vector<ScalarType> vector_a( 1.0, 0.0, 0.0 );
  Geometry::Vector<ScalarType> vector_b( 1.0, 1.0, 1.0 );

  Geometry::Vector<ScalarType> cross_product =
    Geometry::LinearAlgebra::computeCrossProduct( vector_a, vector_b );

  Geometry::Vector<ScalarType> ref_cross_product( 0.0, -1.0, 1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of x-axis and y-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createXAxisVector<ScalarType>(),
				     Geometry::createYAxisVector<ScalarType>() );

  ref_cross_product = Geometry::createZAxisVector<ScalarType>();

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of y-axis and z-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createYAxisVector<ScalarType>(),
				     Geometry::createZAxisVector<ScalarType>() );
  ref_cross_product = Geometry::createXAxisVector<ScalarType>();

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of x-axis and z-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createXAxisVector<ScalarType>(),
				     Geometry::createZAxisVector<ScalarType>() );
  ref_cross_product =
    Geometry::createYAxisVector<ScalarType>( Utility::NEGATIVE_DIRECTION );

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeCrossProduct );

//---------------------------------------------------------------------------//
// Check that the inverse of a matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeMatrixInverse,
				   ScalarType )
{
  Geometry::Matrix<ScalarType> matrix( 1.0, 2.0, 3.0,
				          1.0, 4.0,
				               1.0 );
  Geometry::Matrix<ScalarType> inverse_matrix =
    Geometry::LinearAlgebra::computeMatrixInverse( matrix );

  Geometry::Matrix<ScalarType> identity_matrix;
  identity_matrix.multiply( 1.0, matrix, false, inverse_matrix, false, 0.0 );

  Geometry::Matrix<ScalarType> ref_identity_matrix;
  ref_identity_matrix.identity();

  UTILITY_TEST_COMPARE_FLOATING_ARRAYS(
				   identity_matrix(),
				   ref_identity_matrix(),
				   Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeMatrixInverse );

//---------------------------------------------------------------------------//
// Check that a 3x3 system of equations can be solved
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   solveSystem,
				   ScalarType )
{
  Geometry::Matrix<ScalarType> A( 5.0, 4.0, 3.0,
				2.0, 0.0, 1.0,
				8.0, 3.0, 1.0 );
  Geometry::Vector<ScalarType> b( 12.0, 3.0, 12.0 );

  Geometry::Vector<ScalarType> x = Geometry::LinearAlgebra::solveSystem( A, b );

  Geometry::Vector<ScalarType> ref_x( 1.0, 1.0, 1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( x(),
				ref_x(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, solveSystem );

//---------------------------------------------------------------------------//
// Check that eigenvalues of a 3x3 symmetric matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeEigenvalues,
				   ScalarType )
{
  // Positive-definite matrix
  Geometry::Matrix<ScalarType> coefficient_matrix( 3.0,
						 0.0, 2.0,
						 0.0, 0.0, 1.0 );

  Geometry::Vector<ScalarType> eigenvalues =
    Geometry::LinearAlgebra::computeEigenvalues( coefficient_matrix );

  Geometry::Vector<ScalarType> ref_eigenvalues( 1.0, 2.0, 3.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // Negative-definite matrix
  coefficient_matrix = Geometry::createMatrix<ScalarType>( -3.0,
							  0.0, -2.0,
							  0.0, 0.0, -1.0 );
  eigenvalues = Geometry::LinearAlgebra::computeEigenvalues( coefficient_matrix);

  ref_eigenvalues = Geometry::createVector<ScalarType>( -3.0, -2.0, -1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeEigenvalues );

//---------------------------------------------------------------------------//
// Check that the eigenvectors of a 3x3 symmetric matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeEigenvaluesAndEigenvectors,
				   ScalarType )
{
  // Positive-definite matrix
  Geometry::Matrix<ScalarType> coef_matrix( 3.0,
					  0.0, 2.0,
					  0.0, 0.0, 1.0 );
  Geometry::Matrix<ScalarType> eigenvectors;

  Geometry::Vector<ScalarType> eigenvalues =
    Geometry::LinearAlgebra::computeEigenvaluesAndEigenvectors( coef_matrix,
							      eigenvectors );

  Geometry::Matrix<ScalarType> ref_eigenvectors( 0.0, 0.0, 1.0,
					       0.0, 1.0, 0.0,
					       1.0, 0.0, 0.0 );
  Geometry::Vector<ScalarType> ref_eigenvalues( 1.0, 2.0, 3.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
  TEST_COMPARE_FLOATING_ARRAYS( eigenvectors(),
				ref_eigenvectors(),
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // Negative-definite matrix
  coef_matrix = Geometry::createMatrix<ScalarType>( -3.0,
						   0.0, -2.0,
						   0.0, 0.0, -1.0 );
  eigenvalues =
    Geometry::LinearAlgebra::computeEigenvaluesAndEigenvectors( coef_matrix,
							     eigenvectors );

  ref_eigenvectors.identity();
  ref_eigenvalues = Geometry::createVector<ScalarType>( -3.0, -2.0, -1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
  TEST_COMPARE_FLOATING_ARRAYS( eigenvectors(),
				ref_eigenvectors(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeEigenvaluesAndEigenvectors );

//---------------------------------------------------------------------------//
// Check that eigenvectors can be aligned with the desired axis
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   realignEigenvectors,
				   ScalarType )
{
  // z-axis alignment to x-axis alignment
  Geometry::Vector<ScalarType> eigenvalues( 1.0, 1.0, 2.0 );
  Geometry::Matrix<ScalarType> eigenvectors;
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::X_AXIS );

  Geometry::Matrix<ScalarType> ref_eigenvectors;
  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // z-axis alignment to y-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 1.0, 2.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Y_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 1, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // y-axis alignment to x-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 2.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::X_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 1 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // y-axis alignment to z-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 2.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Z_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 1, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // x-axis alignment to y-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 2.0, 1.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Y_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 1 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // x-axis alignment to z-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 2.0, 1.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Z_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, realignEigenvectors );

//---------------------------------------------------------------------------//
// end tstLinearAlgebraAlgorithms.cpp
//---------------------------------------------------------------------------//

<commit_msg>Fixed the broken linear algebra algorithm unit test.<commit_after>//---------------------------------------------------------------------------//
//!
//! \file   tstLinearAlgebraAlgorithms.cpp
//! \author Alex Robinson
//! \brief  Linear algebra algorithm unit tests.
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>

// Trilinos Includes
#include <Teuchos_UnitTestHarness.hpp>
#include <Teuchos_ScalarTraits.hpp>

// FRENSIE Includes
#include "Geometry_LinearAlgebraAlgorithms.hpp"
#include "Geometry_VectorHelpers.hpp"
#include "Geometry_MatrixHelpers.hpp"
#include "Utility_UnitTestHarnessExtensions.hpp"

//---------------------------------------------------------------------------//
// Instantiation Macros.
//---------------------------------------------------------------------------//
#define UNIT_TEST_INSTANTIATION( type, name ) \
  TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( type, name, float ) \
  TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( type, name, double )

//---------------------------------------------------------------------------//
// Tests.
//---------------------------------------------------------------------------//
// Check that the cross product of two vectors can be calculated
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeCrossProduct,
				   ScalarType )
{
  // cross product of general vectors
  Geometry::Vector<ScalarType> vector_a( 1.0, 0.0, 0.0 );
  Geometry::Vector<ScalarType> vector_b( 1.0, 1.0, 1.0 );

  Geometry::Vector<ScalarType> cross_product =
    Geometry::LinearAlgebra::computeCrossProduct( vector_a, vector_b );

  Geometry::Vector<ScalarType> ref_cross_product( 0.0, -1.0, 1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of x-axis and y-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createXAxisVector<ScalarType>(),
				     Geometry::createYAxisVector<ScalarType>() );

  ref_cross_product = Geometry::createZAxisVector<ScalarType>();

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of y-axis and z-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createYAxisVector<ScalarType>(),
				     Geometry::createZAxisVector<ScalarType>() );
  ref_cross_product = Geometry::createXAxisVector<ScalarType>();

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // cross product of x-axis and z-axis
  cross_product = Geometry::LinearAlgebra::computeCrossProduct(
				     Geometry::createXAxisVector<ScalarType>(),
				     Geometry::createZAxisVector<ScalarType>() );
  ref_cross_product =
    Geometry::createYAxisVector<ScalarType>( Utility::NEGATIVE_DIRECTION );

  TEST_COMPARE_FLOATING_ARRAYS( cross_product,
				ref_cross_product,
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeCrossProduct );

//---------------------------------------------------------------------------//
// Check that the inverse of a matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeMatrixInverse,
				   ScalarType )
{
  Geometry::Matrix<ScalarType> matrix( 1.0, 2.0, 3.0,
				          1.0, 4.0,
				               1.0 );
  Geometry::Matrix<ScalarType> inverse_matrix =
    Geometry::LinearAlgebra::computeMatrixInverse( matrix );

  Geometry::Matrix<ScalarType> identity_matrix;
  identity_matrix.multiply( 1.0, matrix, false, inverse_matrix, false, 0.0 );

  Geometry::Matrix<ScalarType> ref_identity_matrix;
  ref_identity_matrix.identity();

  UTILITY_TEST_COMPARE_FLOATING_ARRAYS(
				   identity_matrix(),
				   ref_identity_matrix(),
				   Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeMatrixInverse );

//---------------------------------------------------------------------------//
// Check that a 3x3 system of equations can be solved
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   solveSystem,
				   ScalarType )
{
  Geometry::Matrix<ScalarType> A( 5.0, 4.0, 3.0,
				2.0, 0.0, 1.0,
				8.0, 3.0, 1.0 );
  Geometry::Vector<ScalarType> b( 12.0, 3.0, 12.0 );

  Geometry::Vector<ScalarType> x = Geometry::LinearAlgebra::solveSystem( A, b );

  Geometry::Vector<ScalarType> ref_x( 1.0, 1.0, 1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( x(),
				ref_x(),
				2*Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, solveSystem );

//---------------------------------------------------------------------------//
// Check that eigenvalues of a 3x3 symmetric matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeEigenvalues,
				   ScalarType )
{
  // Positive-definite matrix
  Geometry::Matrix<ScalarType> coefficient_matrix( 3.0,
						 0.0, 2.0,
						 0.0, 0.0, 1.0 );

  Geometry::Vector<ScalarType> eigenvalues =
    Geometry::LinearAlgebra::computeEigenvalues( coefficient_matrix );

  Geometry::Vector<ScalarType> ref_eigenvalues( 1.0, 2.0, 3.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // Negative-definite matrix
  coefficient_matrix = Geometry::createMatrix<ScalarType>( -3.0,
							  0.0, -2.0,
							  0.0, 0.0, -1.0 );
  eigenvalues = Geometry::LinearAlgebra::computeEigenvalues( coefficient_matrix);

  ref_eigenvalues = Geometry::createVector<ScalarType>( -3.0, -2.0, -1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeEigenvalues );

//---------------------------------------------------------------------------//
// Check that the eigenvectors of a 3x3 symmetric matrix can be computed
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   computeEigenvaluesAndEigenvectors,
				   ScalarType )
{
  // Positive-definite matrix
  Geometry::Matrix<ScalarType> coef_matrix( 3.0,
					  0.0, 2.0,
					  0.0, 0.0, 1.0 );
  Geometry::Matrix<ScalarType> eigenvectors;

  Geometry::Vector<ScalarType> eigenvalues =
    Geometry::LinearAlgebra::computeEigenvaluesAndEigenvectors( coef_matrix,
							      eigenvectors );

  Geometry::Matrix<ScalarType> ref_eigenvectors( 0.0, 0.0, 1.0,
					       0.0, 1.0, 0.0,
					       1.0, 0.0, 0.0 );
  Geometry::Vector<ScalarType> ref_eigenvalues( 1.0, 2.0, 3.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
  TEST_COMPARE_FLOATING_ARRAYS( eigenvectors(),
				ref_eigenvectors(),
				Teuchos::ScalarTraits<ScalarType>::prec() );

  // Negative-definite matrix
  coef_matrix = Geometry::createMatrix<ScalarType>( -3.0,
						   0.0, -2.0,
						   0.0, 0.0, -1.0 );
  eigenvalues =
    Geometry::LinearAlgebra::computeEigenvaluesAndEigenvectors( coef_matrix,
							     eigenvectors );

  ref_eigenvectors.identity();
  ref_eigenvalues = Geometry::createVector<ScalarType>( -3.0, -2.0, -1.0 );

  TEST_COMPARE_FLOATING_ARRAYS( eigenvalues(),
				ref_eigenvalues(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
  TEST_COMPARE_FLOATING_ARRAYS( eigenvectors(),
				ref_eigenvectors(),
				Teuchos::ScalarTraits<ScalarType>::prec() );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, computeEigenvaluesAndEigenvectors );

//---------------------------------------------------------------------------//
// Check that eigenvectors can be aligned with the desired axis
TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( LinearAlgebra,
				   realignEigenvectors,
				   ScalarType )
{
  // z-axis alignment to x-axis alignment
  Geometry::Vector<ScalarType> eigenvalues( 1.0, 1.0, 2.0 );
  Geometry::Matrix<ScalarType> eigenvectors;
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::X_AXIS );

  Geometry::Matrix<ScalarType> ref_eigenvectors;
  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // z-axis alignment to y-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 1.0, 2.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Y_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 1, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // y-axis alignment to x-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 2.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::X_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 1 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // y-axis alignment to z-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 1.0, 2.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Z_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 1, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // x-axis alignment to y-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 2.0, 1.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Y_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 1 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );

  // x-axis alignment to z-axis alignment
  eigenvalues = Geometry::createVector<ScalarType>( 2.0, 1.0, 1.0 );
  eigenvectors.identity();

  Geometry::LinearAlgebra::realignEigenvectors( eigenvalues,
					      eigenvectors,
					      Utility::Z_AXIS );

  ref_eigenvectors.identity();
  ref_eigenvectors.swapColumns( 0, 2 );

  TEST_EQUALITY( eigenvectors, ref_eigenvectors );
}

UNIT_TEST_INSTANTIATION( LinearAlgebra, realignEigenvectors );

//---------------------------------------------------------------------------//
// end tstLinearAlgebraAlgorithms.cpp
//---------------------------------------------------------------------------//

<|endoftext|>


<commit_before>// Author: http://lemming.life
// Language: C++
// Purpose: Example of object oriented programming using classes.
// - C++ is C with classes.
// - Classes enable "encapsulation", meaning everything needed for x is in one package/class.
// - Has clases, enum, interface, abstract class, inheritance, polymorphism, access modifiers,

#include <iostream>
#include <string>
using namespace std;
//using std::string;

class Simple {
    public:
        int x;  // This is a public member variable
        void changeX(int newX) { x = newX; } // This is a public method
};

class Simple2 {
    private:
        int x; // Essential, a private member is one that only the class can access
    public:
        void changeX(int newX) { x = newX; } // changeX is a public method, which can access that x
};

// An enum is a type that has constant members.
enum Color { RED, GREEN, BLUE };

// A simple structure for x and y values.
struct Position {
    int x;
    int y;

    string toString() {
        return "x: " + to_string(x) + " y: " + to_string(y);
    }
};

// An interface is a constract
// - Those using the interface must implement the methods advertised.
class PositionInterface {
    virtual int getX() = 0;
    virtual int getY() = 0;
    virtual void changePosition(int, int) = 0;
};

// Shape is an abstract class
// - Abstract classes cannot be instantiated, but those inheriting from can be instantiated.
// - The setColor(Color aColor) = 0 makes this an "abstract class" in c++
class Shape : PositionInterface {
    private:
        Position position; // The shape has a position structure, it is private and thus only Shape can access
    protected:
        Color color; // Shape and child classes can access
    public:

        // This is a parameterized constructor
        Shape(int newX, int newY) {
            position.x = newX;
            position.y = newY;
        }

        // Implementation of the PositionInterface methods
        int getX() { return position.x; }
        int getY() { return position.y; }
        void changePosition(int newX, int newY) { position.x = newX; position.y = newY; }

        // Shape by itself doesn't have a color
        // but the inherited types may. So, we must
        // ensure that inherited classes implement.
        virtual Color getColor() { return color; }   // virtual method, it can be called but must be done so explictly
        virtual void setColor(Color aColor) = 0;    // A pure virtual method == the child must implement.

        virtual string toString() {
            string colorStr;

            // The getColor() method call will be the child one (Circle)
            // - Polymorphism is magic.
            switch(getColor()) {
                case RED: colorStr = "Red"; break;
                case GREEN: colorStr = "Green"; break;
                case BLUE: colorStr = "Blue"; break;
                default: colorStr = "None"; break;
            }
            return position.toString() + " Color: " + colorStr;
        }

        // Abstract classes must have virtual destructors
        virtual ~Shape() { }
};

// Circle inherits from Shape
// - This is a is-a relationship. Circle "is-a" shape.
class Circle : public Shape {
    private:
        int radius;
    public:
        // A parameterized constructor that calls Shape constructor for the x and y positions.
        Circle(int newX, int newY, int newRadius) : Shape(newX, newY) {
            radius = newRadius;
        }

        Color getColor() {
            return Shape::getColor(); // Explicitly calling the abstract class getColor();
        }

        void setColor(Color aColor) {
            color = aColor; // Circle can access protected and public members
        }

        string toString() {
            return Shape::toString() + " Radius: " + to_string(radius);
        }

        ~Circle() {
            // This is the Circle destructor
            // then ~Shape destructor is called
        }
};

class Square : public Shape {
    private:
        int width;
        int height;
    public:

        // Repeating ourselves is tedious...
        // Seeing repetition like this means that we should reconsider
        // how we have implemented. Perhaps it is the wrong approach.
        // One fix is to delegate this to Shape as a "has-a" relationship
        // in a similar way as we have done with a "has-a" position.
        Color getColor() { return color; }
        void setColor(Color aColor) { color = aColor; }

        string toString() {
            return Shape::toString() + " Width: " + to_string(width) + " Height: " + to_string(height);
        }

        Square(int newX, int newY, int newWidth, int newHeight) : Shape(newX, newY) {
            width = newWidth;
            height = newHeight;
        }
        ~Square() { }
};

int main() {

    Simple simple;
    simple.x = 5;
    cout << "Simple x is " << simple.x << '\n'; // 5
    simple.changeX(10);
    cout << "Simple x is " << simple.x << '\n'; // 10

    // Create an object.
    Circle* circle = new Circle(0, 0, 5);
    cout << circle->toString() << '\n'; // Color not yet assigned. So output is x: 0 y: 0 Color: None Radius: 5
    circle->setColor(RED);
    cout << circle->toString() << '\n'; // x: 0 y: 0 Color: Red Radius: 5
    delete circle; // Order of destructor call is from specific to base.

    // Imagine having a collection of Circles, then we have a collection of Squares.
    // Then we go through each collection, and call the toString() method of each element.
    // Wouldn't it be better if we had a single collection of Squares and Circles -- a collection of Shapes.
    // We can do this because Shape* is an abstract class and both Squares and Circles inherit from it.
    Shape* shapes[] = { new Circle(1, 2, 3), new Square(4, 5, 6, 7) }; // This may also be called "program to an interface"
    int length = 2;

    for(int i=0; i<length; ++i) {
        // Polymorphism: it knows which to call Circle::toString() or Square::toString()
        cout << shapes[i]->toString() << '\n';
        delete shapes[i]; // Delete the object. In C++ we must free memory manually.
    }

    return 0;
}<commit_msg>Added RTTI example.<commit_after>// Author: http://lemming.life
// Language: C++
// Purpose: Example of object oriented programming using classes.
// - C++ is C with classes.
// - Classes enable "encapsulation", meaning everything needed for x is in one package/class.
// - Has clases, enum, interface, abstract class, inheritance, polymorphism, access modifiers,

#include <iostream>
#include <string>
using namespace std;
//using std::string;

class Simple {
    public:
        int x;  // This is a public member variable
        void changeX(int newX) { x = newX; } // This is a public method
};

class Simple2 {
    private:
        int x; // Essential, a private member is one that only the class can access
    public:
        void changeX(int newX) { x = newX; } // changeX is a public method, which can access that x
};

// An enum is a type that has constant members.
enum Color { RED, GREEN, BLUE };

// A simple structure for x and y values.
struct Position {
    int x;
    int y;

    string toString() {
        return "x: " + to_string(x) + " y: " + to_string(y);
    }
};

// An interface is a constract
// - Those using the interface must implement the methods advertised.
class PositionInterface {
    virtual int getX() = 0;
    virtual int getY() = 0;
    virtual void changePosition(int, int) = 0;
};

// Shape is an abstract class
// - Abstract classes cannot be instantiated, but those inheriting from can be instantiated.
// - The setColor(Color aColor) = 0 makes this an "abstract class" in c++
class Shape : PositionInterface {
    private:
        Position position; // The shape has a position structure, it is private and thus only Shape can access
    protected:
        Color color; // Shape and child classes can access
    public:

        // This is a parameterized constructor
        Shape(int newX, int newY) {
            position.x = newX;
            position.y = newY;
        }

        // Implementation of the PositionInterface methods
        int getX() { return position.x; }
        int getY() { return position.y; }
        void changePosition(int newX, int newY) { position.x = newX; position.y = newY; }

        // Shape by itself doesn't have a color
        // but the inherited types may. So, we must
        // ensure that inherited classes implement.
        virtual Color getColor() { return color; }   // virtual method, it can be called but must be done so explictly
        virtual void setColor(Color aColor) = 0;    // A pure virtual method == the child must implement.

        virtual string toString() {
            string colorStr;

            // The getColor() method call will be the child one (Circle)
            // - Polymorphism is magic.
            switch(getColor()) {
                case RED: colorStr = "Red"; break;
                case GREEN: colorStr = "Green"; break;
                case BLUE: colorStr = "Blue"; break;
                default: colorStr = "None"; break;
            }
            return position.toString() + " Color: " + colorStr;
        }

        // Abstract classes must have virtual destructors
        virtual ~Shape() { }
};

// Circle inherits from Shape
// - This is a is-a relationship. Circle "is-a" shape.
class Circle : public Shape {
    private:
        int radius;
    public:
        // A parameterized constructor that calls Shape constructor for the x and y positions.
        Circle(int newX, int newY, int newRadius) : Shape(newX, newY) {
            radius = newRadius;
        }

        ~Circle() {
            // This is the Circle destructor
            // then ~Shape destructor is called
        }

        Color getColor() {
            return Shape::getColor(); // Explicitly calling the abstract class getColor();
        }

        void setColor(Color aColor) {
            color = aColor; // Circle can access protected and public members
        }

        double getArea() { return 3.13 * (radius * radius); }

        string toString() {
            return Shape::toString() + " Radius: " + to_string(radius);
        }
};

class Rectangle : public Shape {
    private:
        int width;
        int height;
    public:

        Rectangle(int newX, int newY, int newWidth, int newHeight) : Shape(newX, newY) {
            width = newWidth;
            height = newHeight;
        }

        ~Rectangle() { }

        // Repeating ourselves is tedious...
        // Seeing repetition like this means that we should reconsider
        // how we have implemented. Perhaps it is the wrong approach.
        // One fix is to delegate this to Shape as a "has-a" relationship
        // in a similar way as we have done with a "has-a" position.
        Color getColor() { return color; }
        void setColor(Color aColor) { color = aColor; }

        double getArea() { return width * height; }

        string toString() {
            return Shape::toString() + " Width: " + to_string(width) + " Height: " + to_string(height);
        }
};

int main() {

    Simple simple;
    simple.x = 5;
    cout << "Simple x is " << simple.x << '\n'; // 5
    simple.changeX(10);
    cout << "Simple x is " << simple.x << '\n'; // 10

    // Create an object.
    Circle* circle = new Circle(0, 0, 5);
    cout << circle->toString() << '\n'; // Color not yet assigned. So output is x: 0 y: 0 Color: None Radius: 5
    circle->setColor(RED);
    cout << circle->toString() << '\n'; // x: 0 y: 0 Color: Red Radius: 5
    delete circle; // Order of destructor call is from specific to base.
    circle = NULL;

    // Imagine having a collection of Circles, then we have a collection of Squares.
    // Then we go through each collection, and call the toString() method of each element.
    // Wouldn't it be better if we had a single collection of Squares and Circles -- a collection of Shapes.
    // We can do this because Shape* is an abstract class and both Squares and Circles inherit from it.
    Shape* shapes[] = { new Circle(1, 2, 3), new Rectangle(4, 5, 6, 7) }; // This may also be called "program to an interface"
    int length = 2;

    for(int i=0; i<length; ++i) {
        // Polymorphism: it knows which to call Circle::toString() or Square::toString()
        cout << shapes[i]->toString() << '\n';
    }

    // To determine what type something is we use Run-Time Type Information (RTTI)
    for (int i=0; i<length; ++i) {
        if (typeid(*shapes[i]) == typeid(Circle)) {
            // Cast it
            Circle* circlePtr = dynamic_cast<Circle*>(shapes[i]);
            cout << "Area of circle: " << circlePtr->getArea() << '\n';
        } else if(typeid(*shapes[i]) == typeid(Rectangle)) {
            Rectangle* rectanglePtr = dynamic_cast<Rectangle*>(shapes[i]);
            cout << "Area of rectangle: " << rectanglePtr->getArea() << '\n';
        }
    }

    for (int i=0; i<length; ++i) {
         delete shapes[i]; // Delete the object. In C++ we must free memory manually.
         shapes[i] = NULL;
    }

    return 0;
}<|endoftext|>


<commit_before>#include "Mocks.hpp"

TEST(json_t, Class) {
    formatter::json_t fmt;
    UNUSED(fmt);
}

TEST(json_t, FormatSingleAttribute) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message"
    };

    formatter::json_t fmt;
    std::string expected = "{\"message\":\"le message\"}";
    EXPECT_EQ(expected, fmt.format(record));
}

TEST(json_t, FormatMultipleAttributes) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message",
        keyword::timestamp() = 100500
    };

    formatter::json_t fmt;
    std::string actual = fmt.format(record);

    rapidjson::Document doc;
    doc.Parse<0>(actual.c_str());
    EXPECT_STREQ("le message", doc["message"].GetString());
    EXPECT_EQ(100500, doc["timestamp"].GetInt());
}

TEST(json_t, FormatMultipleComplexAttributes) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message",
        keyword::timestamp() = 100500,
        attribute::make("@source", "udp://127.0.0.1"),
        attribute::make("@source_host", "dhcp-218-248-wifi.yandex"),
        attribute::make("@source_path", "service/storage"),
        attribute::make("@uuid", "550e8400-e29b-41d4-a716-446655440000")
    };

    formatter::json_t fmt;
    std::string actual = fmt.format(record);

    rapidjson::Document doc;
    doc.Parse<0>(actual.c_str());
    EXPECT_STREQ("le message", doc["message"].GetString());
    EXPECT_EQ(100500, doc["timestamp"].GetInt());
    EXPECT_STREQ("udp://127.0.0.1", doc["@source"].GetString());

    EXPECT_STREQ("udp://127.0.0.1", doc["@source"].GetString());
    EXPECT_STREQ("dhcp-218-248-wifi.yandex", doc["@source_host"].GetString());
    EXPECT_STREQ("service/storage", doc["@source_path"].GetString());
    EXPECT_STREQ("550e8400-e29b-41d4-a716-446655440000", doc["@uuid"].GetString());
}
<commit_msg>Typo fix.<commit_after>#include "Mocks.hpp"

TEST(json_t, Class) {
    formatter::json_t fmt;
    UNUSED(fmt);
}

TEST(json_t, FormatSingleAttribute) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message"
    };

    formatter::json_t fmt;
    std::string expected = "{\"message\":\"le message\"}";
    EXPECT_EQ(expected, fmt.format(record));
}

TEST(json_t, FormatMultipleAttributes) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message",
        keyword::timestamp() = 100500
    };

    formatter::json_t fmt;
    std::string actual = fmt.format(record);

    rapidjson::Document doc;
    doc.Parse<0>(actual.c_str());
    EXPECT_STREQ("le message", doc["message"].GetString());
    EXPECT_EQ(100500, doc["timestamp"].GetInt());
}

TEST(json_t, FormatMultipleComplexAttributes) {
    log::record_t record;
    record.attributes = {
        keyword::message() = "le message",
        keyword::timestamp() = 100500,
        attribute::make("@source", "udp://127.0.0.1"),
        attribute::make("@source_host", "dhcp-218-248-wifi.yandex"),
        attribute::make("@source_path", "service/storage"),
        attribute::make("@uuid", "550e8400-e29b-41d4-a716-446655440000")
    };

    formatter::json_t fmt;
    std::string actual = fmt.format(record);

    rapidjson::Document doc;
    doc.Parse<0>(actual.c_str());
    EXPECT_STREQ("le message", doc["message"].GetString());
    EXPECT_EQ(100500, doc["timestamp"].GetInt());
    EXPECT_STREQ("udp://127.0.0.1", doc["@source"].GetString());
    EXPECT_STREQ("dhcp-218-248-wifi.yandex", doc["@source_host"].GetString());
    EXPECT_STREQ("service/storage", doc["@source_path"].GetString());
    EXPECT_STREQ("550e8400-e29b-41d4-a716-446655440000", doc["@uuid"].GetString());
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Toni Gundogdu.
 *
 * This file is part of cclive.
 *
 * cclive 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.
 *
 * cclive 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 <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifndef S_IRGRP
# define S_IRGRP    S_IRUSR
#endif

#ifndef S_IROTH
# define S_IROTH    S_IRUSR
#endif

#include "except.h"
#include "macros.h"
#include "opts.h"
#include "log.h"

// LogBuffer

LogBuffer::LogBuffer(const int& fd, const bool& close_after)
    : fd(fd), verbose(true), close_after(close_after)
{
    setp(buffer, buffer+(BufferSize-1));
}

LogBuffer::~LogBuffer() {
    sync();
    if (close_after)
        close(fd);
}

void
LogBuffer::setVerbose(const bool& verbose) {
    this->verbose = verbose;
}

int
LogBuffer::flushBuffer() {
    if (!verbose)
        return EOF;
    const int n = pptr() - pbase();
    if (write(fd,buffer,n) != n)
        return EOF;
    pbump(-n);
    return n;
}

std::streambuf::int_type
LogBuffer::overflow(std::streambuf::int_type c) {
    if (c != EOF) {
        *pptr() = c;
        pbump(1);
    }
    if (flushBuffer() == EOF)
        return EOF;
    return c;
}

int
LogBuffer::sync() {
    if (flushBuffer() == EOF)
        return -1;
    return 0;
}

// LogMgr

LogMgr::LogMgr()
    : lbout(NULL), lberr(NULL), oscout(NULL), oscerr(NULL),
      rc(CCLIVE_OK), fname("")
{
    _init();
}

    // Keeps -Weffc++ happy.
LogMgr::LogMgr(const LogMgr&)
    : lbout(NULL), lberr(NULL), oscout(NULL), oscerr(NULL),
      rc(CCLIVE_OK), fname("")
{
    _init();
}

    // Ditto.
LogMgr&
LogMgr::operator=(const LogMgr&) {
    return *this;
}

void
LogMgr::_init(const std::string& fname) {

    this->fname = fname;

    _DELETE(lbout);
    _DELETE(lberr);
    _DELETE(oscout);
    _DELETE(oscerr);

    int fdout = fileno(stdout),
        fderr = fileno(stderr);

    bool close_after = false;

    if (!fname.empty()) {
        const int fd = open(
                fname.c_str(),
                O_WRONLY|O_CREAT|O_TRUNC,
                S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
            );
        if (fd == -1) {
#ifdef HAVE_STRERROR
            fprintf(stderr, "error: %s: %s\n",
                fname.c_str(), strerror(errno));
#else
            perror("open");
#endif
            exit (CCLIVE_SYSTEM);
        }
        fderr = fdout = fd;
        close_after = true;
    }

    lbout = new LogBuffer(fdout, close_after);
    lberr = new LogBuffer(fderr, close_after);

    oscout = new std::ostream(lbout);
    oscerr = new std::ostream(lberr);
}

void
LogMgr::init() {
    const Options opts = optsmgr.getOptions();

    if (opts.background_given) {
#if defined(HAVE_FORK) && defined(HAVE_WORKING_FORK)
        _init(opts.logfile_arg);
#endif
    }
    else {
        lbout->setVerbose(!opts.quiet_given);
        lberr->setVerbose(!opts.quiet_given);
    }
}

LogMgr::~LogMgr() {
    _DELETE(oscerr);
    _DELETE(oscout);
    _DELETE(lberr);
    _DELETE(lbout);
}

std::ostream&
LogMgr::cout() const {
    return *oscout;
}

std::ostream&
LogMgr::cerr() const {
    return *oscerr;
}

std::ostream&
LogMgr::cerr(const RuntimeException& except,
             const bool& prepend_newline /*=true*/)
{
    if (prepend_newline)
        *oscerr << "\n";

    *oscerr << "error: " << except.what() << std::endl;

    rc = except.getReturnCode();

    return *oscerr;
}

std::ostream&
LogMgr::cerr(const std::string& what,
             const bool& prepend_newline, /*=true*/
             const bool& prepend_error, /*=true*/
             const bool& append_newline /*=true*/)
{
    if (prepend_newline)
        *oscerr << "\n";

    if (prepend_error)
        *oscerr << "error: ";

    *oscerr << what;

    if (append_newline)
        *oscerr << std::endl;
    else
        *oscerr << std::flush;

    return *oscerr;
}

const ReturnCode&
LogMgr::getReturnCode() const {
    return rc;
}

void
LogMgr::resetReturnCode() {
    rc = CCLIVE_OK;
}

<commit_msg>log.cpp: errno.h -> cerrno.<commit_after>/*
 * Copyright (C) 2009 Toni Gundogdu.
 *
 * This file is part of cclive.
 *
 * cclive 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.
 *
 * cclive 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 <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifndef S_IRGRP
# define S_IRGRP    S_IRUSR
#endif

#ifndef S_IROTH
# define S_IROTH    S_IRUSR
#endif

#include "except.h"
#include "macros.h"
#include "opts.h"
#include "log.h"

// LogBuffer

LogBuffer::LogBuffer(const int& fd, const bool& close_after)
    : fd(fd), verbose(true), close_after(close_after)
{
    setp(buffer, buffer+(BufferSize-1));
}

LogBuffer::~LogBuffer() {
    sync();
    if (close_after)
        close(fd);
}

void
LogBuffer::setVerbose(const bool& verbose) {
    this->verbose = verbose;
}

int
LogBuffer::flushBuffer() {
    if (!verbose)
        return EOF;
    const int n = pptr() - pbase();
    if (write(fd,buffer,n) != n)
        return EOF;
    pbump(-n);
    return n;
}

std::streambuf::int_type
LogBuffer::overflow(std::streambuf::int_type c) {
    if (c != EOF) {
        *pptr() = c;
        pbump(1);
    }
    if (flushBuffer() == EOF)
        return EOF;
    return c;
}

int
LogBuffer::sync() {
    if (flushBuffer() == EOF)
        return -1;
    return 0;
}

// LogMgr

LogMgr::LogMgr()
    : lbout(NULL), lberr(NULL), oscout(NULL), oscerr(NULL),
      rc(CCLIVE_OK), fname("")
{
    _init();
}

    // Keeps -Weffc++ happy.
LogMgr::LogMgr(const LogMgr&)
    : lbout(NULL), lberr(NULL), oscout(NULL), oscerr(NULL),
      rc(CCLIVE_OK), fname("")
{
    _init();
}

    // Ditto.
LogMgr&
LogMgr::operator=(const LogMgr&) {
    return *this;
}

void
LogMgr::_init(const std::string& fname) {

    this->fname = fname;

    _DELETE(lbout);
    _DELETE(lberr);
    _DELETE(oscout);
    _DELETE(oscerr);

    int fdout = fileno(stdout),
        fderr = fileno(stderr);

    bool close_after = false;

    if (!fname.empty()) {
        const int fd = open(
                fname.c_str(),
                O_WRONLY|O_CREAT|O_TRUNC,
                S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
            );
        if (fd == -1) {
#ifdef HAVE_STRERROR
            fprintf(stderr, "error: %s: %s\n",
                fname.c_str(), strerror(errno));
#else
            perror("open");
#endif
            exit (CCLIVE_SYSTEM);
        }
        fderr = fdout = fd;
        close_after = true;
    }

    lbout = new LogBuffer(fdout, close_after);
    lberr = new LogBuffer(fderr, close_after);

    oscout = new std::ostream(lbout);
    oscerr = new std::ostream(lberr);
}

void
LogMgr::init() {
    const Options opts = optsmgr.getOptions();

    if (opts.background_given) {
#if defined(HAVE_FORK) && defined(HAVE_WORKING_FORK)
        _init(opts.logfile_arg);
#endif
    }
    else {
        lbout->setVerbose(!opts.quiet_given);
        lberr->setVerbose(!opts.quiet_given);
    }
}

LogMgr::~LogMgr() {
    _DELETE(oscerr);
    _DELETE(oscout);
    _DELETE(lberr);
    _DELETE(lbout);
}

std::ostream&
LogMgr::cout() const {
    return *oscout;
}

std::ostream&
LogMgr::cerr() const {
    return *oscerr;
}

std::ostream&
LogMgr::cerr(const RuntimeException& except,
             const bool& prepend_newline /*=true*/)
{
    if (prepend_newline)
        *oscerr << "\n";

    *oscerr << "error: " << except.what() << std::endl;

    rc = except.getReturnCode();

    return *oscerr;
}

std::ostream&
LogMgr::cerr(const std::string& what,
             const bool& prepend_newline, /*=true*/
             const bool& prepend_error, /*=true*/
             const bool& append_newline /*=true*/)
{
    if (prepend_newline)
        *oscerr << "\n";

    if (prepend_error)
        *oscerr << "error: ";

    *oscerr << what;

    if (append_newline)
        *oscerr << std::endl;
    else
        *oscerr << std::flush;

    return *oscerr;
}

const ReturnCode&
LogMgr::getReturnCode() const {
    return rc;
}

void
LogMgr::resetReturnCode() {
    rc = CCLIVE_OK;
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/xml_parse.hpp"
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/thread/mutex.hpp>
#include <cstdlib>

using boost::bind;
using namespace libtorrent;

address_v4 lsd::lsd_multicast_address;
udp::endpoint lsd::lsd_multicast_endpoint;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(0)
	, m_socket(ios)
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
	// Bittorrent Local discovery multicast address and port
	lsd_multicast_address = address_v4::from_string("239.192.152.143");
	lsd_multicast_endpoint = udp::endpoint(lsd_multicast_address, 6771);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
	assert(lsd_multicast_address.is_multicast());
	rebind(listen_interface);
}

lsd::~lsd() {}

void lsd::rebind(address const& listen_interface)
{
	address_v4 local_ip;
	if (listen_interface.is_v4() && listen_interface != address_v4::from_string("0.0.0.0"))
	{
		local_ip = listen_interface.to_v4();
	}
	else
	{
		// make a best guess of the interface we're using and its IP
		udp::resolver r(m_socket.io_service());
		udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
		for (;i != udp::resolver_iterator(); ++i)
		{
			if (i->endpoint().address().is_v4()) break;
		}

		if (i == udp::resolver_iterator())
		{
	#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
			m_log << "local host name did not resolve to an IPv4 address. "
				"disabling local service discovery" << std::endl;
	#endif
			m_disabled = true;
			return;
		}

		local_ip = i->endpoint().address().to_v4();
	}

	try
	{
		// the local interface hasn't changed
		if (m_socket.is_open()
			&& m_socket.local_endpoint().address() == local_ip)
			return;

		m_socket.close();

		using namespace asio::ip::multicast;

		m_socket.open(udp::v4());
		m_socket.set_option(datagram_socket::reuse_address(true));
		m_socket.bind(udp::endpoint(local_ip, 6771));

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "local ip: " << local_ip << std::endl;
#endif

		m_socket.set_option(join_group(lsd_multicast_address));
		m_socket.set_option(outbound_interface(address_v4()));
		m_socket.set_option(enable_loopback(false));
		m_socket.set_option(hops(255));
	}
	catch (std::exception& e)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "socket multicast error " << e.what()
			<< ". disabling local service discovery" << std::endl;
#endif
		m_disabled = true;
		return;
	}
	m_disabled = false;

	setup_receive();
}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << listen_port << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 0;
	asio::error_code ec;
	m_socket.send_to(asio::buffer(msg.c_str(), msg.size() - 1)
		, lsd_multicast_endpoint, ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg));
}

void lsd::resend_announce(asio::error_code const& e, std::string msg) try
{
	if (e) return;

	m_socket.send_to(asio::buffer(msg, msg.size() - 1)
		, lsd_multicast_endpoint);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg));
}
catch (std::exception&)
{}

void lsd::on_announce(asio::error_code const& e
	, std::size_t bytes_transferred)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== on_announce" << std::endl;
#endif
	using namespace libtorrent::detail;
	if (e) return;

	char* p = m_receive_buffer;
	char* end = m_receive_buffer + bytes_transferred;
	char* line = std::find(p, end, '\n');
	for (char* i = p; i < line; ++i) *i = std::tolower(*i);
	if (line == end || std::strcmp("bt-search", p))
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== Got incorrect method in announce" << std::string(p, line) << std::endl;
#endif
		setup_receive();
		return;
	}
	p = line + 1;
	int port = 0;
	sha1_hash ih(0);
	while (p != end)
	{
		line = std::find(p, end, '\n');
		if (line == end) break;
		*line = 0;
		for (char* i = p; i < line; ++i) *i = std::tolower(*i);
		if (!strcmp(p, "port:"))
		{
			port = atoi(p + 5);
		}
		else if (!strcmp(p, "infohash:"))
		{
			ih = boost::lexical_cast<sha1_hash>(p + 9);
		}
		p = line + 1;
	}

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== Got incoming local announce " << m_remote.address()
			<< ":" << port << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
		try { m_callback(tcp::endpoint(m_remote.address(), port), ih); }
		catch (std::exception&) {}
	}
	setup_receive();
}

void lsd::setup_receive() try
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " *** setup_receive" << std::endl;
#endif
	assert(m_socket.is_open());
	m_socket.async_receive_from(asio::buffer(m_receive_buffer
		, sizeof(m_receive_buffer)), m_remote, bind(&lsd::on_announce, this, _1, _2));
}
catch (std::exception&)
{}

void lsd::close()
{
	m_socket.close();
}

<commit_msg>fixed typo in last check in<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/xml_parse.hpp"
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/thread/mutex.hpp>
#include <cstdlib>

using boost::bind;
using namespace libtorrent;

address_v4 lsd::lsd_multicast_address;
udp::endpoint lsd::lsd_multicast_endpoint;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(0)
	, m_socket(ios)
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
	// Bittorrent Local discovery multicast address and port
	lsd_multicast_address = address_v4::from_string("239.192.152.143");
	lsd_multicast_endpoint = udp::endpoint(lsd_multicast_address, 6771);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
	assert(lsd_multicast_address.is_multicast());
	rebind(listen_interface);
}

lsd::~lsd() {}

void lsd::rebind(address const& listen_interface)
{
	address_v4 local_ip;
	if (listen_interface.is_v4() && listen_interface != address_v4::from_string("0.0.0.0"))
	{
		local_ip = listen_interface.to_v4();
	}
	else
	{
		// make a best guess of the interface we're using and its IP
		udp::resolver r(m_socket.io_service());
		udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
		for (;i != udp::resolver_iterator(); ++i)
		{
			if (i->endpoint().address().is_v4()) break;
		}

		if (i == udp::resolver_iterator())
		{
	#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
			m_log << "local host name did not resolve to an IPv4 address. "
				"disabling local service discovery" << std::endl;
	#endif
			m_disabled = true;
			return;
		}

		local_ip = i->endpoint().address().to_v4();
	}

	try
	{
		// the local interface hasn't changed
		if (m_socket.is_open()
			&& m_socket.local_endpoint().address() == local_ip)
			return;

		m_socket.close();

		using namespace asio::ip::multicast;

		m_socket.open(udp::v4());
		m_socket.set_option(datagram_socket::reuse_address(true));
		m_socket.bind(udp::endpoint(local_ip, 6771));

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "local ip: " << local_ip << std::endl;
#endif

		m_socket.set_option(join_group(lsd_multicast_address));
		m_socket.set_option(outbound_interface(address_v4()));
		m_socket.set_option(enable_loopback(false));
		m_socket.set_option(hops(255));
	}
	catch (std::exception& e)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "socket multicast error " << e.what()
			<< ". disabling local service discovery" << std::endl;
#endif
		m_disabled = true;
		return;
	}
	m_disabled = false;

	setup_receive();
}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << listen_port << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 0;
	asio::error_code ec;
	m_socket.send_to(asio::buffer(msg.c_str(), msg.size() - 1)
		, lsd_multicast_endpoint, 0, ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg));
}

void lsd::resend_announce(asio::error_code const& e, std::string msg) try
{
	if (e) return;

	m_socket.send_to(asio::buffer(msg, msg.size() - 1)
		, lsd_multicast_endpoint);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg));
}
catch (std::exception&)
{}

void lsd::on_announce(asio::error_code const& e
	, std::size_t bytes_transferred)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== on_announce" << std::endl;
#endif
	using namespace libtorrent::detail;
	if (e) return;

	char* p = m_receive_buffer;
	char* end = m_receive_buffer + bytes_transferred;
	char* line = std::find(p, end, '\n');
	for (char* i = p; i < line; ++i) *i = std::tolower(*i);
	if (line == end || std::strcmp("bt-search", p))
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== Got incorrect method in announce" << std::string(p, line) << std::endl;
#endif
		setup_receive();
		return;
	}
	p = line + 1;
	int port = 0;
	sha1_hash ih(0);
	while (p != end)
	{
		line = std::find(p, end, '\n');
		if (line == end) break;
		*line = 0;
		for (char* i = p; i < line; ++i) *i = std::tolower(*i);
		if (!strcmp(p, "port:"))
		{
			port = atoi(p + 5);
		}
		else if (!strcmp(p, "infohash:"))
		{
			ih = boost::lexical_cast<sha1_hash>(p + 9);
		}
		p = line + 1;
	}

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " <== Got incoming local announce " << m_remote.address()
			<< ":" << port << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
		try { m_callback(tcp::endpoint(m_remote.address(), port), ih); }
		catch (std::exception&) {}
	}
	setup_receive();
}

void lsd::setup_receive() try
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " *** setup_receive" << std::endl;
#endif
	assert(m_socket.is_open());
	m_socket.async_receive_from(asio::buffer(m_receive_buffer
		, sizeof(m_receive_buffer)), m_remote, bind(&lsd::on_announce, this, _1, _2));
}
catch (std::exception&)
{}

void lsd::close()
{
	m_socket.close();
}

<|endoftext|>