liquidityinfo.cpp

NuBit / src / liquidityinfo.cpp

// Copyright (c) 2014-2015 The Nu developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/algorithm/string.hpp>

#include "liquidityinfo.h"
#include "main.h"
#include "ui_interface.h"

#define LIQUIDITY_INFO_MAX_HEIGHT 260000 // Liquidity info from custodians who were elected more than LIQUIDITY_INFO_MAX_HEIGHT blocks ago are ignored

#ifdef TESTING
#define MAX_LIQUIDITY_INFO_PER_CUSTODIAN 5
#else
#define MAX_LIQUIDITY_INFO_PER_CUSTODIAN 100
#endif

using namespace std;

map<const CLiquiditySource, CLiquidityInfo> mapLiquidityInfo;
CCriticalSection cs_mapLiquidityInfo;
int64 nLastLiquidityUpdate = 0;

void RemoveExtraLiquidityInfo(const CCustodianAddress& custodianAddress, unsigned char cUnit)
{
    LOCK(cs_mapLiquidityInfo);

    int nCount = 0;
    map<int64, map<const CLiquiditySource, CLiquidityInfo>::iterator> mapLiquidityInfoByTime;

    map<const CLiquiditySource, CLiquidityInfo>::iterator it = mapLiquidityInfo.begin();
    while (it != mapLiquidityInfo.end())
    {
        const CLiquidityInfo& info = it->second;
        const CCustodianAddress& infoAddress = info.GetCustodianAddress();

        if (infoAddress == custodianAddress && cUnit == info.cUnit)
        {
            nCount++;
            mapLiquidityInfoByTime[info.nTime] = it;
        }
        it++;
    }

    if (nCount && nCount > MAX_LIQUIDITY_INFO_PER_CUSTODIAN)
    {
        const map<const CLiquiditySource, CLiquidityInfo>::iterator& oldestit = mapLiquidityInfoByTime.begin()->second;
        const CLiquidityInfo& oldestInfo = oldestit->second;
        printf("Custodian %s has too many liquidity info. Removing the oldest one: %s\n", custodianAddress.GetAddress().ToString().c_str(), oldestInfo.ToString().c_str());
        mapLiquidityInfo.erase(oldestit);
    }
}

bool CLiquidityInfo::ProcessLiquidityInfo()
{
    CCustodianAddress address(GetCustodianAddress());

    {
        LOCK(cs_main);

        if (pindexBest->nProtocolVersion >= PROTOCOL_V2_0 && nVersion > pindexBest->nProtocolVersion)
        {
            if (fDebug)
                printf("Liquidity info version is above protocol version\n");
            return false;
        }

        map<CCustodianAddress, CBlockIndex*> mapElectedCustodian = pindexBest->GetElectedCustodians();
        map<CCustodianAddress, CBlockIndex*>::iterator it;
        it = mapElectedCustodian.find(address);
        if (it == mapElectedCustodian.end())
        {
            if (fDebug)
                printf("Custodian not found in elected ones\n");
            return false;
        }

        CBlockIndex *pindex = it->second;
        if (!pindexBest || pindexBest->nHeight - pindex->nHeight > LIQUIDITY_INFO_MAX_HEIGHT)
        {
            if (fDebug)
                printf("Custodian is too old\n");
            return false;
        }
    }

    if (!CheckSignature())
    {
        if (fDebug)
            printf("Invalid liquidity info signature\n");
        return false;
    }

    if (!IsValid())
    {
        if (fDebug)
            printf("Liquidity info is invalid\n");
        return false;
    }

    CLiquiditySource source(address, cUnit, sIdentifier);

    {
        LOCK(cs_mapLiquidityInfo);

        map<const CLiquiditySource, CLiquidityInfo>::iterator it;
        it = mapLiquidityInfo.find(source);
        if (it != mapLiquidityInfo.end())
        {
            if (it->second.nTime >= nTime)
            {
                if (fDebug)
                    printf("Previous liquidity info has the same time or is more recent\n");
                return false;
            }
        }

        mapLiquidityInfo[source] = *this;
        nLastLiquidityUpdate = GetAdjustedTime();
    }

    printf("accepted liquidity info from %s\n", address.GetAddress().ToString().c_str());

    RemoveExtraLiquidityInfo(address, cUnit);

    MainFrameRepaint();
    return true;
}


bool CUnsignedLiquidityInfo::IsValid() const
{
    if (sIdentifier.length() > 255)
        return false;

    for (std::string::size_type i=0; i<sIdentifier.length(); ++i)
    {
        const unsigned char& chr = sIdentifier[i];
        if (chr < 0x20 || chr > 0x7e) // only printable ASCII is allowed
            return false;
    }

    return true;
}

string CUnsignedLiquidityInfo::GetTier() const
{
    vector<string> items;
    boost::split(items, sIdentifier, boost::is_any_of(":"));

    if (items.size() > 0)
    {
        const string& tierString = items[0];
        if (!tierString.empty() && tierString.find_first_not_of("0123456789") == std::string::npos)
            return tierString;
    }
    return "";
}

void RemoveExpiredLiquidityInfo(int nCurrentHeight)
{
    bool fAnyRemoved = false;
    {
        LOCK2(cs_main, cs_mapLiquidityInfo);

        map<const CLiquiditySource, CLiquidityInfo>::iterator it;
        map<CCustodianAddress, CBlockIndex*> mapElectedCustodian = pindexBest->GetElectedCustodians();
        it = mapLiquidityInfo.begin();
        while (it != mapLiquidityInfo.end())
        {
            const CCustodianAddress& address = it->first.custodianAddress;
            CBlockIndex *pindex = NULL;

            map<CCustodianAddress, CBlockIndex*>::const_iterator electedIt = mapElectedCustodian.find(address);
            if (electedIt != mapElectedCustodian.end())
                pindex = electedIt->second;

            if (!pindex || nCurrentHeight - pindex->nHeight > LIQUIDITY_INFO_MAX_HEIGHT)
            {
                mapLiquidityInfo.erase(it++);
                fAnyRemoved = true;
                nLastLiquidityUpdate = GetAdjustedTime();
            }
            else
                it++;
        }
    }

    if (fAnyRemoved)
        MainFrameRepaint();
}