text:
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/vinniefalco/json
//

// Test that header file is self-contained.
#include <boost/json/value_to.hpp>

#include "test_suite.hpp"

#include <map>
#include <unordered_map>
#include <vector>

namespace boost {
namespace json {

class value_to_test
{
public:

    template<class T>
    void
    check(T t)
    {
        BOOST_TEST(value_to<T>(value_from(t)) == t);
    }

    void
    testNumberCast()
    {
        check((short)-1);
        check((int)-2);
        check((long)-3);
        check((long long)-4);
        check((unsigned short)1);
        check((unsigned int)2);
        check((unsigned long)3);
        check((unsigned long long)4);
        check((float)1.5);
        check((double)2.5);
        check((long double)3.5);
        check(true);
    }

    void
    testJsonTypes()
    {
        value_to<object>(value(object_kind));
        value_to<array>(value(array_kind));
        value_to<string>(value(string_kind));
    }

    void
    testGenerics()
    {
        check(std::string("test"));
        check(std::map<std::string, int>
        {
            {"a", 1}, {"b", 2}, {"c", 3}
        });
        check(std::unordered_map<std::string, int>
        {
            { "a", 1 }, {"b", 2}, {"c", 3}
        });
        check(std::vector<int>{1, 2, 3, 4});
    }

    void
    run()
    {
        testNumberCast();
        testJsonTypes();
        testGenerics();
    }
};

TEST_SUITE(value_to_test, "boost.json.value_to");

} // json
} // boost

ext: cpp

max_stars_repo_name: AeroStun/json

max_issues_repo_name: AeroStun/json

max_forks_repo_name: AeroStun/json


text:
//
// StrawWaveform represents post-amplification voltage as a function of time at one end of a
// a straw, over the time period of 1 microbunch.  It includes all physical and electronics
// effects prior to digitization.
//
// Original author David Brown, LBNL
//
#include "TrackerMC/inc/StrawWaveform.hh"
#include <math.h>
#include <boost/math/special_functions/binomial.hpp>

using namespace std;
namespace mu2e {
  using namespace TrkTypes;
  namespace TrackerMC {
    StrawWaveform::StrawWaveform(StrawClusterSequence const& hseq, XTalk const& xtalk) :
      _cseq(hseq), _xtalk(xtalk), _sid(hseq.strawId())
    {}

    StrawWaveform::StrawWaveform(StrawWaveform const& other) : _cseq(other._cseq),
    _xtalk(other._xtalk), _sid(other._sid)
    {}

    bool StrawWaveform::crossesThreshold(StrawElectronics const& strawele,double threshold,WFX& wfx) const {
      bool retval(false);
      // make sure we start past the input time
      while(wfx._iclust != _cseq.clustList().end() && wfx._iclust->time()-strawele.clusterLookbackTime()< wfx._time ){
	++(wfx._iclust);
      }
      // loop till we're at the end or we go over threshold
      if(wfx._iclust != _cseq.clustList().end()){
	// sample initial voltage for this clust
	wfx._vstart = sampleWaveform(strawele,StrawElectronics::thresh,wfx._iclust->time()-strawele.clusterLookbackTime());
	// if we start above threhold, scan forward till we're below
	if(wfx._vstart > threshold)
	  returnCrossing(strawele,threshold, wfx);
	// scan ahead quickly from there to where there's enough integral charge to possibly cross threshold.
	if(roughCrossing(strawele,threshold,wfx)) {
	  // start the fine scan from this clust.
	  while(wfx._iclust != _cseq.clustList().end() ){
	    // First, get the starting voltage
	    wfx._vstart = sampleWaveform(strawele,StrawElectronics::thresh,wfx._iclust->time()-strawele.clusterLookbackTime());
	    //// check if this clust could cross threshold
	    //if(wfx._vstart + maxLinearResponse(wfx._iclust) > threshold){
	      // check the actual response
	      double maxtime = wfx._iclust->time()+strawele.maxResponseTime(StrawElectronics::thresh,wfx._iclust->wireDistance());
	      double maxresp = sampleWaveform(strawele,StrawElectronics::thresh,maxtime);
	      if(maxresp > threshold){
		// interpolate to find the precise crossing
		fineCrossing(strawele,threshold,maxresp,wfx);

		retval = true;
		break;
	      }
            //}
	    // advance to next clust
	    ++(wfx._iclust);
	  }
	}
      }
      return retval;
    }

    void StrawWaveform::returnCrossing(StrawElectronics const& strawele, double threshold, WFX& wfx) const {
      while(wfx._iclust != _cseq.clustList().end() && wfx._vstart > threshold) {
	// move forward in time at least as twice the time to the maxium for this clust
	double time = wfx._iclust->time()+strawele.clusterLookbackTime() + 2*strawele.maxResponseTime(StrawElectronics::thresh,wfx._iclust->wireDistance());
	while(wfx._iclust != _cseq.clustList().end() &&
	    wfx._iclust->time()-strawele.clusterLookbackTime() < time){
	  ++(wfx._iclust);
	}
	if(wfx._iclust != _cseq.clustList().end()){
	  wfx._vstart = sampleWaveform(strawele,StrawElectronics::thresh,wfx._iclust->time()-strawele.clusterLookbackTime());
	  wfx._time =wfx._iclust->time()-strawele.clusterLookbackTime();
	}
      }
    }

    bool StrawWaveform::roughCrossing(StrawElectronics const& strawele, double threshold, WFX& wfx) const {
      // add voltage till we go over threshold by simple linear sum.  That's a minimum requirement
      // for actually crossing threshold
      double resp = wfx._vstart;
      while(wfx._iclust != _cseq.clustList().end()){
	resp += maxLinearResponse(strawele,wfx._iclust);
	if(resp > threshold)break;
	++(wfx._iclust);
      }
      // update time
      if(wfx._iclust != _cseq.clustList().end() )
	wfx._time = wfx._iclust->time()-strawele.clusterLookbackTime();

      return wfx._iclust != _cseq.clustList().end() && resp > threshold;
    }

    bool StrawWaveform::fineCrossing(StrawElectronics const& strawele, double threshold,double maxresp, WFX& wfx) const {
      static double timestep(0.020); // interpolation minimum to use linear threshold crossing calculation
      double pretime = wfx._iclust->time()-strawele.clusterLookbackTime();
      double posttime = pretime + strawele.clusterLookbackTime() + strawele.maxResponseTime(StrawElectronics::thresh,wfx._iclust->wireDistance());
      double presample = wfx._vstart;
      double postsample = maxresp;
      static const unsigned maxstep(10); // 10 steps max
      unsigned nstep(0);
      double deltat = posttime-pretime;
      double dt = deltat;
      double slope = deltat/(postsample-presample);
      double time = pretime + slope*(threshold-presample);
      // linear interpolation
      while(fabs(dt) > timestep && nstep < maxstep) {
	double sample = sampleWaveform(strawele,StrawElectronics::thresh,time);
	if(sample > threshold){
	  posttime = time;
	  postsample = sample;
	} else {
	  pretime = time;
	  presample = sample;
	}
	deltat = posttime-pretime;
	slope = deltat/(postsample-presample);
	double oldtime = time;
	time = pretime + slope*(threshold-presample);
	dt = time-oldtime;
	++nstep;
      }
      // set crossing time
      wfx._time = time;
      // record the threshold
      wfx._vcross = threshold;
      // update the referenced clust: this can be different than the one we started with!
      while(wfx._iclust != _cseq.clustList().end() &&
	  wfx._iclust->time()-strawele.clusterLookbackTime() < wfx._time){
	++(wfx._iclust);
      }
      // back off one
      if(wfx._iclust != _cseq.clustList().begin())--(wfx._iclust);

      // apply dispersion effects.  This changes the slope of the voltage response FIXME!!!

      // return on convergence
      return dt < timestep;
    }

    double StrawWaveform::maxLinearResponse(StrawElectronics const& strawele,StrawClusterList::const_iterator const& iclust) const {
      // ignore saturation effects
      double linresp = strawele.maxLinearResponse(_sid,StrawElectronics::thresh,iclust->wireDistance(),iclust->charge());
      linresp *= (_xtalk._preamp + _xtalk._postamp);
      return linresp;
    }

    double StrawWaveform::sampleWaveform(StrawElectronics const& strawele,StrawElectronics::Path ipath,double time) const {
      // loop over all clusts and add their response at this time
      StrawClusterList const& hlist = _cseq.clustList();
      double linresp(0.0);
      auto iclust = hlist.begin();
      while(iclust != hlist.end() && iclust->time()-strawele.clusterLookbackTime() < time){
	// compute the linear straw electronics response to this charge.  This is pre-saturation
	linresp += strawele.linearResponse(_sid,ipath,time-iclust->time(),iclust->charge(),iclust->wireDistance());
	// move to next clust
	++iclust;
      }
      double totresp = linresp * _xtalk._postamp;
      if(_xtalk._preamp>0.0)
	totresp += _xtalk._preamp*linresp;
      return totresp;
    }

    void StrawWaveform::sampleADCWaveform(StrawElectronics const& strawele,ADCTimes const& times,ADCVoltages& volts) const {
      volts.clear();
      volts.reserve(times.size());
      if (_xtalk._dest != _xtalk._source){
        //FIXME doesn't deal with cross talk hits yet
        for (size_t j=0;j<times.size();j++){
          volts.push_back(0);
        }
        return;
      }

      // check if going to be saturated
      double max_possible_voltage = 0;
      for (auto iclust = _cseq.clustList().begin();iclust != _cseq.clustList().end();++iclust){
        max_possible_voltage += maxLinearResponse(strawele,iclust);
      }
      if (max_possible_voltage > strawele.saturationVoltage()){
        // create waveform of threshold circuit output
        // step along waveform and apply saturation
        // for each time, get contribution from each step in waveform using impulse response

        // skip to the first cluster that matters for the first adc time
        auto iclust = _cseq.clustList().begin();
        while (iclust != _cseq.clustList().end()){
          double time = iclust->time()-strawele.clusterLookbackTime();
          if (time + strawele.truncationTime(StrawElectronics::thresh) > times[0])
            break;
          else
            ++iclust;
        }

        // step through time
        for (size_t j=0;j<times.size();j++){
          volts.push_back(0);
        }

        int num_steps = (int)ceil((times[times.size()-1]-iclust->time()-strawele.clusterLookbackTime())/strawele.saturationTimeStep());

        for (int i=0;i<num_steps;i++){
          double time = iclust->time()-strawele.clusterLookbackTime() + i*strawele.saturationTimeStep();
          // sum up the preamp response at this step
          double response = 0;
          auto jclust = iclust;
          while(jclust != _cseq.clustList().end() && jclust->time()-strawele.clusterLookbackTime() < time){
            response += strawele.linearResponse(_sid,StrawElectronics::thresh,time-jclust->time(),jclust->charge(),jclust->wireDistance(),true);
            ++jclust;
          }
          // now saturate it
          double sat_response = strawele.saturatedResponse(response);
          // then calculate the impulse response at each of the adctimes and add it to that
          for (size_t j=0;j<times.size();j++){
            // this function includes multiplication by number of steps in saturationTimeStep
            volts[j] += strawele.adcImpulseResponse(_sid,times[j]-time,sat_response);
          }
        }
      }else{
        for(auto itime=times.begin();itime!=times.end();++itime){
          volts.push_back(sampleWaveform(strawele,StrawElectronics::adc,*itime));
        }
      }
    }

  unsigned short StrawWaveform::digitizeTOT(StrawElectronics const& strawele, double threshold, double time) const {
      for (size_t i=1;i<strawele.maxTOT();i++){
        if (sampleWaveform(strawele,StrawElectronics::thresh,time + i*strawele.totLSB()) < threshold)
          return static_cast<unsigned short>(i);
      }
      return static_cast<unsigned short>(strawele.maxTOT());
    }
  }

}

ext: cc


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

#include "activecoralnode.h"
#include "addrman.h"
#include "coralnode.h"
#include "coralnodeman.h"
#include "main.h"
#include "obfuscation.h"
#include "spork.h"
#include "util.h"
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>

#define MN_WINNER_MINIMUM_AGE 8000 // Age in seconds. This should be > CORALNODE_REMOVAL_SECONDS to avoid misconfigured new nodes in the list.

/** coralnode manager */
CCoralnodeMan mnodeman;

struct CompareLastPaid {
    bool operator()(const pair<int64_t, CTxIn>& t1,
        const pair<int64_t, CTxIn>& t2) const
    {
        return t1.first < t2.first;
    }
};

struct CompareScoreTxIn {
    bool operator()(const pair<int64_t, CTxIn>& t1,
        const pair<int64_t, CTxIn>& t2) const
    {
        return t1.first < t2.first;
    }
};

struct CompareScoreMN {
    bool operator()(const pair<int64_t, CCoralnode>& t1,
        const pair<int64_t, CCoralnode>& t2) const
    {
        return t1.first < t2.first;
    }
};

//
// CCoralnodeDB
//

CCoralnodeDB::CCoralnodeDB()
{
    pathMN = GetDataDir() / "fncache.dat";
    strMagicMessage = "CoralnodeCache";
}

bool CCoralnodeDB::Write(const CCoralnodeMan& mnodemanToSave)
{
    int64_t nStart = GetTimeMillis();

    // serialize, checksum data up to that point, then append checksum
    CDataStream ssCoralnodes(SER_DISK, CLIENT_VERSION);
    ssCoralnodes << strMagicMessage;                   // coralnode cache file specific magic message
    ssCoralnodes << FLATDATA(Params().MessageStart()); // network specific magic number
    ssCoralnodes << mnodemanToSave;
    uint256 hash = Hash(ssCoralnodes.begin(), ssCoralnodes.end());
    ssCoralnodes << hash;

    // open output file, and associate with CAutoFile
    FILE* file = fopen(pathMN.string().c_str(), "wb");
    CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
    if (fileout.IsNull())
        return error("%s : Failed to open file %s", __func__, pathMN.string());

    // Write and commit header, data
    try {
        fileout << ssCoralnodes;
    } catch (std::exception& e) {
        return error("%s : Serialize or I/O error - %s", __func__, e.what());
    }
    //    FileCommit(fileout);
    fileout.fclose();

    LogPrint("coralnode", "Written info to mncache.dat  %dms\n", GetTimeMillis() - nStart);
    LogPrint("coralnode", "  %s\n", mnodemanToSave.ToString());

    return true;
}

CCoralnodeDB::ReadResult CCoralnodeDB::Read(CCoralnodeMan& mnodemanToLoad, bool fDryRun)
{
    int64_t nStart = GetTimeMillis();
    // open input file, and associate with CAutoFile
    FILE* file = fopen(pathMN.string().c_str(), "rb");
    CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
    if (filein.IsNull()) {
        error("%s : Failed to open file %s", __func__, pathMN.string());
        return FileError;
    }

    // use file size to size memory buffer
    int fileSize = boost::filesystem::file_size(pathMN);
    int dataSize = fileSize - sizeof(uint256);
    // Don't try to resize to a negative number if file is small
    if (dataSize < 0)
        dataSize = 0;
    vector<unsigned char> vchData;
    vchData.resize(dataSize);
    uint256 hashIn;

    // read data and checksum from file
    try {
        filein.read((char*)&vchData[0], dataSize);
        filein >> hashIn;
    } catch (std::exception& e) {
        error("%s : Deserialize or I/O error - %s", __func__, e.what());
        return HashReadError;
    }
    filein.fclose();

    CDataStream ssCoralnodes(vchData, SER_DISK, CLIENT_VERSION);

    // verify stored checksum matches input data
    uint256 hashTmp = Hash(ssCoralnodes.begin(), ssCoralnodes.end());
    if (hashIn != hashTmp) {
        error("%s : Checksum mismatch, data corrupted", __func__);
        return IncorrectHash;
    }

    unsigned char pchMsgTmp[4];
    std::string strMagicMessageTmp;
    try {
        // de-serialize file header (coralnode cache file specific magic message) and ..

        ssCoralnodes >> strMagicMessageTmp;

        // ... verify the message matches predefined one
        if (strMagicMessage != strMagicMessageTmp) {
            error("%s : Invalid coralnode cache magic message", __func__);
            return IncorrectMagicMessage;
        }

        // de-serialize file header (network specific magic number) and ..
        ssCoralnodes >> FLATDATA(pchMsgTmp);

        // ... verify the network matches ours
        if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
            error("%s : Invalid network magic number", __func__);
            return IncorrectMagicNumber;
        }
        // de-serialize data into CCoralnodeMan object
        ssCoralnodes >> mnodemanToLoad;
    } catch (std::exception& e) {
        mnodemanToLoad.Clear();
        error("%s : Deserialize or I/O error - %s", __func__, e.what());
        return IncorrectFormat;
    }

    LogPrint("coralnode", "Loaded info from mncache.dat  %dms\n", GetTimeMillis() - nStart);
    LogPrint("coralnode", "  %s\n", mnodemanToLoad.ToString());
    if (!fDryRun) {
        LogPrint("coralnode", "Coralnode manager - cleaning....\n");
        mnodemanToLoad.CheckAndRemove(true);
        LogPrint("coralnode", "Coralnode manager - result:\n");
        LogPrint("coralnode", "  %s\n", mnodemanToLoad.ToString());
    }

    return Ok;
}

void DumpCoralnodes()
{
    int64_t nStart = GetTimeMillis();

    CCoralnodeDB mndb;
    CCoralnodeMan tempMnodeman;

    LogPrint("coralnode", "Verifying mncache.dat format...\n");
    CCoralnodeDB::ReadResult readResult = mndb.Read(tempMnodeman, true);
    // there was an error and it was not an error on file opening => do not proceed
    if (readResult == CCoralnodeDB::FileError)
        LogPrint("coralnode", "Missing coralnode cache file - mncache.dat, will try to recreate\n");
    else if (readResult != CCoralnodeDB::Ok) {
        LogPrint("coralnode", "Error reading mncache.dat: ");
        if (readResult == CCoralnodeDB::IncorrectFormat)
            LogPrint("coralnode", "magic is ok but data has invalid format, will try to recreate\n");
        else {
            LogPrint("coralnode", "file format is unknown or invalid, please fix it manually\n");
            return;
        }
    }
    LogPrint("coralnode", "Writting info to mncache.dat...\n");
    mndb.Write(mnodeman);

    LogPrint("coralnode", "Coralnode dump finished  %dms\n", GetTimeMillis() - nStart);
}

CCoralnodeMan::CCoralnodeMan()
{
    nDsqCount = 0;
}

bool CCoralnodeMan::Add(CCoralnode& mn)
{
    LOCK(cs);

    if (!mn.IsEnabled())
        return false;

    CCoralnode* pmn = Find(mn.vin);
    if (pmn == NULL) {
        LogPrint("coralnode", "CCoralnodeMan: Adding new Coralnode %s - %i now\n", mn.vin.prevout.hash.ToString(), size() + 1);
        vCoralnodes.push_back(mn);
        return true;
    }

    return false;
}

void CCoralnodeMan::AskForMN(CNode* pnode, CTxIn& vin)
{
    std::map<COutPoint, int64_t>::iterator i = mWeAskedForCoralnodeListEntry.find(vin.prevout);
    if (i != mWeAskedForCoralnodeListEntry.end()) {
        int64_t t = (*i).second;
        if (GetTime() < t) return; // we've asked recently
    }

    // ask for the fnb info once from the node that sent fnp

    LogPrint("coralnode", "CCoralnodeMan::AskForMN - Asking node for missing entry, vin: %s\n", vin.prevout.hash.ToString());
    pnode->PushMessage("obseg", vin);
    int64_t askAgain = GetTime() + CORALNODE_MIN_MNP_SECONDS;
    mWeAskedForCoralnodeListEntry[vin.prevout] = askAgain;
}

void CCoralnodeMan::Check()
{
    LOCK(cs);

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();
    }
}

void CCoralnodeMan::CheckAndRemove(bool forceExpiredRemoval)
{
    Check();

    LOCK(cs);

    //remove inactive and outdated
    vector<CCoralnode>::iterator it = vCoralnodes.begin();
    while (it != vCoralnodes.end()) {
        if ((*it).activeState == CCoralnode::CORALNODE_REMOVE ||
            (*it).activeState == CCoralnode::CORALNODE_VIN_SPENT ||
            (forceExpiredRemoval && (*it).activeState == CCoralnode::CORALNODE_EXPIRED) ||
            (*it).protocolVersion < coralnodePayments.GetMinCoralnodePaymentsProto()) {
            LogPrint("coralnode", "CCoralnodeMan: Removing inactive Coralnode %s - %i now\n", (*it).vin.prevout.hash.ToString(), size() - 1);

            //erase all of the broadcasts we've seen from this vin
            // -- if we missed a few pings and the node was removed, this will allow is to get it back without them
            //    sending a brand new fnb
            map<uint256, CCoralnodeBroadcast>::iterator it3 = mapSeenCoralnodeBroadcast.begin();
            while (it3 != mapSeenCoralnodeBroadcast.end()) {
                if ((*it3).second.vin == (*it).vin) {
                    coralnodeSync.mapSeenSyncMNB.erase((*it3).first);
                    mapSeenCoralnodeBroadcast.erase(it3++);
                } else {
                    ++it3;
                }
            }

            // allow us to ask for this coralnode again if we see another ping
            map<COutPoint, int64_t>::iterator it2 = mWeAskedForCoralnodeListEntry.begin();
            while (it2 != mWeAskedForCoralnodeListEntry.end()) {
                if ((*it2).first == (*it).vin.prevout) {
                    mWeAskedForCoralnodeListEntry.erase(it2++);
                } else {
                    ++it2;
                }
            }

            it = vCoralnodes.erase(it);
        } else {
            ++it;
        }
    }

    // check who's asked for the Coralnode list
    map<CNetAddr, int64_t>::iterator it1 = mAskedUsForCoralnodeList.begin();
    while (it1 != mAskedUsForCoralnodeList.end()) {
        if ((*it1).second < GetTime()) {
            mAskedUsForCoralnodeList.erase(it1++);
        } else {
            ++it1;
        }
    }

    // check who we asked for the Coralnode list
    it1 = mWeAskedForCoralnodeList.begin();
    while (it1 != mWeAskedForCoralnodeList.end()) {
        if ((*it1).second < GetTime()) {
            mWeAskedForCoralnodeList.erase(it1++);
        } else {
            ++it1;
        }
    }

    // check which Coralnodes we've asked for
    map<COutPoint, int64_t>::iterator it2 = mWeAskedForCoralnodeListEntry.begin();
    while (it2 != mWeAskedForCoralnodeListEntry.end()) {
        if ((*it2).second < GetTime()) {
            mWeAskedForCoralnodeListEntry.erase(it2++);
        } else {
            ++it2;
        }
    }

    // remove expired mapSeenCoralnodeBroadcast
    map<uint256, CCoralnodeBroadcast>::iterator it3 = mapSeenCoralnodeBroadcast.begin();
    while (it3 != mapSeenCoralnodeBroadcast.end()) {
        if ((*it3).second.lastPing.sigTime < GetTime() - (CORALNODE_REMOVAL_SECONDS * 2)) {
            mapSeenCoralnodeBroadcast.erase(it3++);
            coralnodeSync.mapSeenSyncMNB.erase((*it3).second.GetHash());
        } else {
            ++it3;
        }
    }

    // remove expired mapSeenCoralnodePing
    map<uint256, CCoralnodePing>::iterator it4 = mapSeenCoralnodePing.begin();
    while (it4 != mapSeenCoralnodePing.end()) {
        if ((*it4).second.sigTime < GetTime() - (CORALNODE_REMOVAL_SECONDS * 2)) {
            mapSeenCoralnodePing.erase(it4++);
        } else {
            ++it4;
        }
    }
}

void CCoralnodeMan::Clear()
{
    LOCK(cs);
    vCoralnodes.clear();
    mAskedUsForCoralnodeList.clear();
    mWeAskedForCoralnodeList.clear();
    mWeAskedForCoralnodeListEntry.clear();
    mapSeenCoralnodeBroadcast.clear();
    mapSeenCoralnodePing.clear();
    nDsqCount = 0;
}

int CCoralnodeMan::stable_size()
{
    int nStable_size = 0;
    int nMinProtocol = ActiveProtocol();
    int64_t nCoralnode_Min_Age = MN_WINNER_MINIMUM_AGE;
    int64_t nCoralnode_Age = 0;

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.protocolVersion < nMinProtocol) {
            continue; // Skip obsolete versions
        }
        if (IsSporkActive(SPORK_8_CORALNODE_PAYMENT_ENFORCEMENT)) {
            nCoralnode_Age = GetAdjustedTime() - mn.sigTime;
            if ((nCoralnode_Age) < nCoralnode_Min_Age) {
                continue; // Skip coralnodes younger than (default) 8000 sec (MUST be > CORALNODE_REMOVAL_SECONDS)
            }
        }
        mn.Check();
        if (!mn.IsEnabled())
            continue; // Skip not-enabled coralnodes

        nStable_size++;
    }

    return nStable_size;
}

int CCoralnodeMan::CountEnabled(int protocolVersion)
{
    int i = 0;
    protocolVersion = protocolVersion == -1 ? coralnodePayments.GetMinCoralnodePaymentsProto() : protocolVersion;

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();
        if (mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue;
        i++;
    }

    return i;
}

void CCoralnodeMan::CountNetworks(int protocolVersion, int& ipv4, int& ipv6, int& onion)
{
    protocolVersion = protocolVersion == -1 ? coralnodePayments.GetMinCoralnodePaymentsProto() : protocolVersion;

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();
        std::string strHost;
        int port;
        SplitHostPort(mn.addr.ToString(), port, strHost);
        CNetAddr node = CNetAddr(strHost, false);
        int nNetwork = node.GetNetwork();
        switch (nNetwork) {
        case 1:
            ipv4++;
            break;
        case 2:
            ipv6++;
            break;
        case 3:
            onion++;
            break;
        }
    }
}

void CCoralnodeMan::DsegUpdate(CNode* pnode)
{
    LOCK(cs);

    if (Params().NetworkID() == CBaseChainParams::MAIN) {
        if (!(pnode->addr.IsRFC1918() || pnode->addr.IsLocal())) {
            std::map<CNetAddr, int64_t>::iterator it = mWeAskedForCoralnodeList.find(pnode->addr);
            if (it != mWeAskedForCoralnodeList.end()) {
                if (GetTime() < (*it).second) {
                    LogPrint("coralnode", "obseg - we already asked peer %i for the list; skipping...\n", pnode->GetId());
                    return;
                }
            }
        }
    }

    pnode->PushMessage("obseg", CTxIn());
    int64_t askAgain = GetTime() + CORALNODES_DSEG_SECONDS;
    mWeAskedForCoralnodeList[pnode->addr] = askAgain;
}

CCoralnode* CCoralnodeMan::Find(const CScript& payee)
{
    LOCK(cs);
    CScript payee2;

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        payee2 = GetScriptForDestination(mn.pubKeyCollateralAddress.GetID());
        if (payee2 == payee)
            return &mn;
    }
    return NULL;
}

CCoralnode* CCoralnodeMan::Find(const CTxIn& vin)
{
    LOCK(cs);

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.vin.prevout == vin.prevout)
            return &mn;
    }
    return NULL;
}

CCoralnode* CCoralnodeMan::Find(const CPubKey& pubKeyCoralnode)
{
    LOCK(cs);

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.pubKeyCoralnode == pubKeyCoralnode)
            return &mn;
    }
    return NULL;
}

//
// Deterministically select the oldest/best coralnode to pay on the network
//
CCoralnode* CCoralnodeMan::GetNextCoralnodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount)
{
    LOCK(cs);

    CCoralnode* pBestCoralnode = NULL;
    std::vector<pair<int64_t, CTxIn> > vecCoralnodeLastPaid;

    /*
        Make a vector with all of the last paid times
    */

    int nMnCount = CountEnabled();
    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();
        if (!mn.IsEnabled()) continue;

        // //check protocol version
        if (mn.protocolVersion < coralnodePayments.GetMinCoralnodePaymentsProto()) continue;

        //it's in the list (up to 8 entries ahead of current block to allow propagation) -- so let's skip it
        if (coralnodePayments.IsScheduled(mn, nBlockHeight)) continue;

        //it's too new, wait for a cycle
        if (fFilterSigTime && mn.sigTime + (nMnCount * 2.6 * 60) > GetAdjustedTime()) continue;

        //make sure it has as many confirmations as there are coralnodes
        if (mn.GetCoralnodeInputAge() < nMnCount) continue;

        vecCoralnodeLastPaid.push_back(make_pair(mn.SecondsSincePayment(), mn.vin));
    }

    nCount = (int)vecCoralnodeLastPaid.size();

    //when the network is in the process of upgrading, don't penalize nodes that recently restarted
    if (fFilterSigTime && nCount < nMnCount / 3) return GetNextCoralnodeInQueueForPayment(nBlockHeight, false, nCount);

    // Sort them high to low
    sort(vecCoralnodeLastPaid.rbegin(), vecCoralnodeLastPaid.rend(), CompareLastPaid());

    // Look at 1/10 of the oldest nodes (by last payment), calculate their scores and pay the best one
    //  -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely
    //  -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2
    //  -- (chance per block * chances before IsScheduled will fire)
    int nTenthNetwork = CountEnabled() / 10;
    int nCountTenth = 0;
    uint256 nHigh = 0;
    BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn) & s, vecCoralnodeLastPaid) {
        CCoralnode* pmn = Find(s.second);
        if (!pmn) break;

        uint256 n = pmn->CalculateScore(1, nBlockHeight - 100);
        if (n > nHigh) {
            nHigh = n;
            pBestCoralnode = pmn;
        }
        nCountTenth++;
        if (nCountTenth >= nTenthNetwork) break;
    }
    return pBestCoralnode;
}

CCoralnode* CCoralnodeMan::FindRandomNotInVec(std::vector<CTxIn>& vecToExclude, int protocolVersion)
{
    LOCK(cs);

    protocolVersion = protocolVersion == -1 ? coralnodePayments.GetMinCoralnodePaymentsProto() : protocolVersion;

    int nCountEnabled = CountEnabled(protocolVersion);
    LogPrint("coralnode", "CCoralnodeMan::FindRandomNotInVec - nCountEnabled - vecToExclude.size() %d\n", nCountEnabled - vecToExclude.size());
    if (nCountEnabled - vecToExclude.size() < 1) return NULL;

    int rand = GetRandInt(nCountEnabled - vecToExclude.size());
    LogPrint("coralnode", "CCoralnodeMan::FindRandomNotInVec - rand %d\n", rand);
    bool found;

    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.protocolVersion < protocolVersion || !mn.IsEnabled()) continue;
        found = false;
        BOOST_FOREACH (CTxIn& usedVin, vecToExclude) {
            if (mn.vin.prevout == usedVin.prevout) {
                found = true;
                break;
            }
        }
        if (found) continue;
        if (--rand < 1) {
            return &mn;
        }
    }

    return NULL;
}

CCoralnode* CCoralnodeMan::GetCurrentCoralNode(int mod, int64_t nBlockHeight, int minProtocol)
{
    int64_t score = 0;
    CCoralnode* winner = NULL;

    // scan for winner
    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();
        if (mn.protocolVersion < minProtocol || !mn.IsEnabled()) continue;

        // calculate the score for each Coralnode
        uint256 n = mn.CalculateScore(mod, nBlockHeight);
        int64_t n2 = n.GetCompact(false);

        // determine the winner
        if (n2 > score) {
            score = n2;
            winner = &mn;
        }
    }

    return winner;
}

int CCoralnodeMan::GetCoralnodeRank(const CTxIn& vin, int64_t nBlockHeight, int minProtocol, bool fOnlyActive)
{
    std::vector<pair<int64_t, CTxIn> > vecCoralnodeScores;
    int64_t nCoralnode_Min_Age = MN_WINNER_MINIMUM_AGE;
    int64_t nCoralnode_Age = 0;

    //make sure we know about this block
    uint256 hash = 0;
    if (!GetBlockHash(hash, nBlockHeight)) return -1;

    // scan for winner
    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.protocolVersion < minProtocol) {
            LogPrint("coralnode", "Skipping Coralnode with obsolete version %d\n", mn.protocolVersion);
            continue; // Skip obsolete versions
        }

        if (IsSporkActive(SPORK_8_CORALNODE_PAYMENT_ENFORCEMENT)) {
            nCoralnode_Age = GetAdjustedTime() - mn.sigTime;
            if ((nCoralnode_Age) < nCoralnode_Min_Age) {
                if (fDebug) LogPrint("coralnode", "Skipping just activated Coralnode. Age: %ld\n", nCoralnode_Age);
                continue; // Skip coralnodes younger than (default) 1 hour
            }
        }
        if (fOnlyActive) {
            mn.Check();
            if (!mn.IsEnabled()) continue;
        }
        uint256 n = mn.CalculateScore(1, nBlockHeight);
        int64_t n2 = n.GetCompact(false);

        vecCoralnodeScores.push_back(make_pair(n2, mn.vin));
    }

    sort(vecCoralnodeScores.rbegin(), vecCoralnodeScores.rend(), CompareScoreTxIn());

    int rank = 0;
    BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn) & s, vecCoralnodeScores) {
        rank++;
        if (s.second.prevout == vin.prevout) {
            return rank;
        }
    }

    return -1;
}

std::vector<pair<int, CCoralnode> > CCoralnodeMan::GetCoralnodeRanks(int64_t nBlockHeight, int minProtocol)
{
    std::vector<pair<int64_t, CCoralnode> > vecCoralnodeScores;
    std::vector<pair<int, CCoralnode> > vecCoralnodeRanks;

    //make sure we know about this block
    uint256 hash = 0;
    if (!GetBlockHash(hash, nBlockHeight)) return vecCoralnodeRanks;

    // scan for winner
    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        mn.Check();

        if (mn.protocolVersion < minProtocol) continue;

        if (!mn.IsEnabled()) {
            vecCoralnodeScores.push_back(make_pair(9999, mn));
            continue;
        }

        uint256 n = mn.CalculateScore(1, nBlockHeight);
        int64_t n2 = n.GetCompact(false);

        vecCoralnodeScores.push_back(make_pair(n2, mn));
    }

    sort(vecCoralnodeScores.rbegin(), vecCoralnodeScores.rend(), CompareScoreMN());

    int rank = 0;
    BOOST_FOREACH (PAIRTYPE(int64_t, CCoralnode) & s, vecCoralnodeScores) {
        rank++;
        vecCoralnodeRanks.push_back(make_pair(rank, s.second));
    }

    return vecCoralnodeRanks;
}

CCoralnode* CCoralnodeMan::GetCoralnodeByRank(int nRank, int64_t nBlockHeight, int minProtocol, bool fOnlyActive)
{
    std::vector<pair<int64_t, CTxIn> > vecCoralnodeScores;

    // scan for winner
    BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
        if (mn.protocolVersion < minProtocol) continue;
        if (fOnlyActive) {
            mn.Check();
            if (!mn.IsEnabled()) continue;
        }

        uint256 n = mn.CalculateScore(1, nBlockHeight);
        int64_t n2 = n.GetCompact(false);

        vecCoralnodeScores.push_back(make_pair(n2, mn.vin));
    }

    sort(vecCoralnodeScores.rbegin(), vecCoralnodeScores.rend(), CompareScoreTxIn());

    int rank = 0;
    BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn) & s, vecCoralnodeScores) {
        rank++;
        if (rank == nRank) {
            return Find(s.second);
        }
    }

    return NULL;
}

void CCoralnodeMan::ProcessCoralnodeConnections()
{
    //we don't care about this for regtest
    if (Params().NetworkID() == CBaseChainParams::REGTEST) return;

    LOCK(cs_vNodes);
    BOOST_FOREACH (CNode* pnode, vNodes) {
        if (pnode->fObfuScationMaster) {
            if (obfuScationPool.pSubmittedToCoralnode != NULL && pnode->addr == obfuScationPool.pSubmittedToCoralnode->addr) continue;
            LogPrint("coralnode", "Closing Coralnode connection peer=%i \n", pnode->GetId());
            pnode->fObfuScationMaster = false;
            pnode->Release();
        }
    }
}

void CCoralnodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
    if (fLiteMode) return; //disable all Obfuscation/Coralnode related functionality
    if (!coralnodeSync.IsBlockchainSynced()) return;

    LOCK(cs_process_message);

    if (strCommand == "fnb") { //Coralnode Broadcast
        CCoralnodeBroadcast fnb;
        vRecv >> fnb;

        if (mapSeenCoralnodeBroadcast.count(fnb.GetHash())) { //seen
            coralnodeSync.AddedCoralnodeList(fnb.GetHash());
            return;
        }
        mapSeenCoralnodeBroadcast.insert(make_pair(fnb.GetHash(), fnb));

        int nDoS = 0;
        if (!fnb.CheckAndUpdate(nDoS)) {
            if (nDoS > 0)
                Misbehaving(pfrom->GetId(), nDoS);

            //failed
            return;
        }

        uint256 hashBlock = 0;
        CTransaction tx;
        // make sure the vout that was signed is related to the transaction that spawned the Coralnode
        //  - this is expensive, so it's only done once per Coralnode
        if (!obfuScationSigner.IsVinAssociatedWithPubkey(fnb.vin, fnb.pubKeyCollateralAddress, tx, hashBlock)) {
            LogPrint("coralnode", "fnb - Got mismatched pubkey and vin\n");
            Misbehaving(pfrom->GetId(), 33);
            return;
        }

        CValidationState state;
        bool fAcceptable = false;
        {
            TRY_LOCK(cs_main, lockMain);
            if (!lockMain) return;
            fAcceptable = AcceptableFundamentalTxn(mempool, state, tx);
        }

        if (!fAcceptable) {
            LogPrint("coralnode", "fnb - Got bad vin, doesnt burn collateral amount\n");
            Misbehaving(pfrom->GetId(), 33);
            return;
        }

        // make sure it's still unspent
        //  - this is checked later by .check() in many places and by ThreadCheckObfuScationPool()
        if (fnb.CheckInputsAndAdd(nDoS)) {
            // use this as a peer
            addrman.Add(CAddress(fnb.addr), pfrom->addr, 2 * 60 * 60);
            coralnodeSync.AddedCoralnodeList(fnb.GetHash());
        } else {
            LogPrint("coralnode", "fnb - Rejected Coralnode entry %s\n", fnb.vin.prevout.hash.ToString());

            if (nDoS > 0)
                Misbehaving(pfrom->GetId(), nDoS);
        }
    }

    else if (strCommand == "fnp") { //Coralnode Ping
        CCoralnodePing fnp;
        vRecv >> fnp;

        LogPrint("coralnode", "fnp - Coralnode ping, vin: %s\n", fnp.vin.prevout.hash.ToString());

        if (mapSeenCoralnodePing.count(fnp.GetHash())) return; //seen
        mapSeenCoralnodePing.insert(make_pair(fnp.GetHash(), fnp));

        int nDoS = 0;
        if (fnp.CheckAndUpdate(nDoS)) return;

        if (nDoS > 0) {
            // if anything significant failed, mark that node
            Misbehaving(pfrom->GetId(), nDoS);
        } else {
            // if nothing significant failed, search existing Coralnode list
            CCoralnode* pmn = Find(fnp.vin);
            // if it's known, don't ask for the fnb, just return
            if (pmn != NULL) return;
        }

        // something significant is broken or mn is unknown,
        // we might have to ask for a coralnode entry once
        AskForMN(pfrom, fnp.vin);

    } else if (strCommand == "obseg") { //Get Coralnode list or specific entry

        CTxIn vin;
        vRecv >> vin;

        if (vin == CTxIn()) { //only should ask for this once
            //local network
            bool isLocal = (pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal());

            if (!isLocal && Params().NetworkID() == CBaseChainParams::MAIN) {
                std::map<CNetAddr, int64_t>::iterator i = mAskedUsForCoralnodeList.find(pfrom->addr);
                if (i != mAskedUsForCoralnodeList.end()) {
                    int64_t t = (*i).second;
                    if (GetTime() < t) {
                        Misbehaving(pfrom->GetId(), 34);
                        LogPrint("coralnode", "obseg - peer already asked me for the list\n");
                        return;
                    }
                }
                int64_t askAgain = GetTime() + CORALNODES_DSEG_SECONDS;
                mAskedUsForCoralnodeList[pfrom->addr] = askAgain;
            }
        } //else, asking for a specific node which is ok

        int nInvCount = 0;

        BOOST_FOREACH (CCoralnode& mn, vCoralnodes) {
            if (mn.addr.IsRFC1918()) continue; //local network

            if (mn.IsEnabled()) {
                LogPrint("coralnode", "obseg - Sending Coralnode entry - %s \n", mn.vin.prevout.hash.ToString());
                if (vin == CTxIn() || vin == mn.vin) {
                    CCoralnodeBroadcast fnb = CCoralnodeBroadcast(mn);
                    uint256 hash = fnb.GetHash();
                    pfrom->PushInventory(CInv(MSG_CORALNODE_ANNOUNCE, hash));
                    nInvCount++;

                    if (!mapSeenCoralnodeBroadcast.count(hash)) mapSeenCoralnodeBroadcast.insert(make_pair(hash, fnb));

                    if (vin == mn.vin) {
                        LogPrint("coralnode", "obseg - Sent 1 Coralnode entry to peer %i\n", pfrom->GetId());
                        return;
                    }
                }
            }
        }

        if (vin == CTxIn()) {
            pfrom->PushMessage("ssc", CORALNODE_SYNC_LIST, nInvCount);
            LogPrint("coralnode", "obseg - Sent %d Coralnode entries to peer %i\n", nInvCount, pfrom->GetId());
        }
    }
    /*
     * IT'S SAFE TO REMOVE THIS IN FURTHER VERSIONS
     * AFTER MIGRATION TO V12 IS DONE
     */

    // Light version for OLD MASSTERNODES - fake pings, no self-activation
    else if (strCommand == "obsee") { //ObfuScation Election Entry

        if (IsSporkActive(SPORK_10_CORALNODE_PAY_UPDATED_NODES)) return;

        CTxIn vin;
        CService addr;
        CPubKey pubkey;
        CPubKey pubkey2;
        vector<unsigned char> vchSig;
        int64_t sigTime;
        int count;
        int current;
        int64_t lastUpdated;
        int protocolVersion;
        CScript donationAddress;
        int donationPercentage;
        std::string strMessage;

        vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion >> donationAddress >> donationPercentage;

        // make sure signature isn't in the future (past is OK)
        if (sigTime > GetAdjustedTime() + 60 * 60) {
            LogPrint("coralnode", "obsee - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString());
            Misbehaving(pfrom->GetId(), 1);
            return;
        }

        std::string vchPubKey(pubkey.begin(), pubkey.end());
        std::string vchPubKey2(pubkey2.begin(), pubkey2.end());

        strMessage = addr.ToString() + boost::lexical_cast<std::string>(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast<std::string>(protocolVersion) + donationAddress.ToString() + boost::lexical_cast<std::string>(donationPercentage);

        if (protocolVersion < coralnodePayments.GetMinCoralnodePaymentsProto()) {
            LogPrint("coralnode", "obsee - ignoring outdated Coralnode %s protocol version %d < %d\n", vin.prevout.hash.ToString(), protocolVersion, coralnodePayments.GetMinCoralnodePaymentsProto());
            Misbehaving(pfrom->GetId(), 1);
            return;
        }

        CScript pubkeyScript;
        pubkeyScript = GetScriptForDestination(pubkey.GetID());

        if (pubkeyScript.size() != 25) {
            LogPrint("coralnode", "obsee - pubkey the wrong size\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        CScript pubkeyScript2;
        pubkeyScript2 = GetScriptForDestination(pubkey2.GetID());

        if (pubkeyScript2.size() != 25) {
            LogPrint("coralnode", "obsee - pubkey2 the wrong size\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        if (!vin.scriptSig.empty()) {
            LogPrint("coralnode", "obsee - Ignore Not Empty ScriptSig %s\n", vin.prevout.hash.ToString());
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        std::string errorMessage = "";
        if (!obfuScationSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)) {
            LogPrint("coralnode", "obsee - Got bad Coralnode address signature\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        if (Params().NetworkID() == CBaseChainParams::MAIN) {
            if (addr.GetPort() != Params().GetDefaultPort()) return;
        } else if (addr.GetPort() == Params().GetDefaultPort())
            return;

        //search existing Coralnode list, this is where we update existing Coralnodes with new obsee broadcasts
        CCoralnode* pmn = this->Find(vin);
        if (pmn != NULL) {
            // count == -1 when it's a new entry
            //   e.g. We don't want the entry relayed/time updated when we're syncing the list
            // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below,
            //   after that they just need to match
            if (count == -1 && pmn->pubKeyCollateralAddress == pubkey && (GetAdjustedTime() - pmn->nLastDsee > CORALNODE_MIN_MNB_SECONDS)) {
                if (pmn->protocolVersion > GETHEADERS_VERSION && sigTime - pmn->lastPing.sigTime < CORALNODE_MIN_MNB_SECONDS) return;
                if (pmn->nLastDsee < sigTime) { //take the newest entry
                    LogPrint("coralnode", "obsee - Got updated entry for %s\n", vin.prevout.hash.ToString());
                    if (pmn->protocolVersion < GETHEADERS_VERSION) {
                        pmn->pubKeyCoralnode = pubkey2;
                        pmn->sigTime = sigTime;
                        pmn->sig = vchSig;
                        pmn->protocolVersion = protocolVersion;
                        pmn->addr = addr;
                        //fake ping
                        pmn->lastPing = CCoralnodePing(vin);
                    }
                    pmn->nLastDsee = sigTime;
                    pmn->Check();
                    if (pmn->IsEnabled()) {
                        TRY_LOCK(cs_vNodes, lockNodes);
                        if (!lockNodes) return;
                        BOOST_FOREACH (CNode* pnode, vNodes)
                            if (pnode->nVersion >= coralnodePayments.GetMinCoralnodePaymentsProto())
                                pnode->PushMessage("obsee", vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage);
                    }
                }
            }

            return;
        }

        static std::map<COutPoint, CPubKey> mapSeenobsee;
        if (mapSeenobsee.count(vin.prevout) && mapSeenobsee[vin.prevout] == pubkey) {
            LogPrint("coralnode", "obsee - already seen this vin %s\n", vin.prevout.ToString());
            return;
        }
        mapSeenobsee.insert(make_pair(vin.prevout, pubkey));
        // make sure the vout that was signed is related to the transaction that spawned the Coralnode
        //  - this is expensive, so it's only done once per Coralnode

        uint256 hashBlock = 0;
        CTransaction tx;

        if (!obfuScationSigner.IsVinAssociatedWithPubkey(vin, pubkey, tx, hashBlock)) {
            LogPrint("coralnode", "obsee - Got mismatched pubkey and vin\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        LogPrint("coralnode", "obsee - Got NEW OLD Coralnode entry %s\n", vin.prevout.hash.ToString());

        // make sure it's still unspent
        //  - this is checked later by .check() in many places and by ThreadCheckObfuScationPool()

        CValidationState state;
        /*CMutableTransaction tx = CMutableTransaction();
        CTxOut vout = CTxOut(9999.99 * COIN, obfuScationPool.collateralPubKey);
        tx.vin.push_back(vin);
        tx.vout.push_back(vout);*/

        bool fAcceptable = false;
        {
            TRY_LOCK(cs_main, lockMain);
            if (!lockMain) return;
            fAcceptable = AcceptableFundamentalTxn(mempool, state, tx);
        }

        if (fAcceptable) {
            if (GetInputAge(vin) < CORALNODE_MIN_CONFIRMATIONS) {
                LogPrint("coralnode", "obsee - Input must have least %d confirmations\n", CORALNODE_MIN_CONFIRMATIONS);
                Misbehaving(pfrom->GetId(), 20);
                return;
            }

            // verify that sig time is legit in past
            // should be at least not earlier than block when 1000 CaritasCoin tx got CORALNODE_MIN_CONFIRMATIONS
            uint256 hashBlock = 0;
            CTransaction tx2;
            GetTransaction(vin.prevout.hash, tx2, hashBlock, true);
            BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
            if (mi != mapBlockIndex.end() && (*mi).second) {
                CBlockIndex* pMNIndex = (*mi).second;                                                             // block for 10000 CaritasCoin tx -> 1 confirmation
                CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + CORALNODE_MIN_CONFIRMATIONS - 1]; // block where tx got CORALNODE_MIN_CONFIRMATIONS
                if (pConfIndex->GetBlockTime() > sigTime) {
                    LogPrint("coralnode", "fnb - Bad sigTime %d for Coralnode %s (%i conf block is at %d)\n",
                        sigTime, vin.prevout.hash.ToString(), CORALNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime());
                    return;
                }
            }

            // use this as a peer
            addrman.Add(CAddress(addr), pfrom->addr, 2 * 60 * 60);

            // add Coralnode
            CCoralnode mn = CCoralnode();
            mn.addr = addr;
            mn.vin = vin;
            mn.pubKeyCollateralAddress = pubkey;
            mn.sig = vchSig;
            mn.sigTime = sigTime;
            mn.pubKeyCoralnode = pubkey2;
            mn.protocolVersion = protocolVersion;
            // fake ping
            mn.lastPing = CCoralnodePing(vin);
            mn.Check(true);
            // add v11 coralnodes, v12 should be added by fnb only
            if (protocolVersion < GETHEADERS_VERSION) {
                LogPrint("coralnode", "obsee - Accepted OLD Coralnode entry %i %i\n", count, current);
                Add(mn);
            }
            if (mn.IsEnabled()) {
                TRY_LOCK(cs_vNodes, lockNodes);
                if (!lockNodes) return;
                BOOST_FOREACH (CNode* pnode, vNodes)
                    if (pnode->nVersion >= coralnodePayments.GetMinCoralnodePaymentsProto())
                        pnode->PushMessage("obsee", vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion, donationAddress, donationPercentage);
            }
        } else {
            LogPrint("coralnode", "obsee - Rejected Coralnode entry %s\n", vin.prevout.hash.ToString());

            int nDoS = 0;
            if (state.IsInvalid(nDoS)) {
                LogPrint("coralnode", "obsee - %s from %i %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(),
                    pfrom->GetId(), pfrom->cleanSubVer.c_str());
                if (nDoS > 0)
                    Misbehaving(pfrom->GetId(), nDoS);
            }
        }
    }

    else if (strCommand == "obseep") { //ObfuScation Election Entry Ping

        if (IsSporkActive(SPORK_10_CORALNODE_PAY_UPDATED_NODES)) return;

        CTxIn vin;
        vector<unsigned char> vchSig;
        int64_t sigTime;
        bool stop;
        vRecv >> vin >> vchSig >> sigTime >> stop;

        //LogPrint("coralnode","obseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false");

        if (sigTime > GetAdjustedTime() + 60 * 60) {
            LogPrint("coralnode", "obseep - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString());
            Misbehaving(pfrom->GetId(), 1);
            return;
        }

        if (sigTime <= GetAdjustedTime() - 60 * 60) {
            LogPrint("coralnode", "obseep - Signature rejected, too far into the past %s - %d %d \n", vin.prevout.hash.ToString(), sigTime, GetAdjustedTime());
            Misbehaving(pfrom->GetId(), 1);
            return;
        }

        std::map<COutPoint, int64_t>::iterator i = mWeAskedForCoralnodeListEntry.find(vin.prevout);
        if (i != mWeAskedForCoralnodeListEntry.end()) {
            int64_t t = (*i).second;
            if (GetTime() < t) return; // we've asked recently
        }

        // see if we have this Coralnode
        CCoralnode* pmn = this->Find(vin);
        if (pmn != NULL && pmn->protocolVersion >= coralnodePayments.GetMinCoralnodePaymentsProto()) {
            // LogPrint("coralnode","obseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str());
            // take this only if it's newer
            if (sigTime - pmn->nLastDseep > CORALNODE_MIN_MNP_SECONDS) {
                std::string strMessage = pmn->addr.ToString() + boost::lexical_cast<std::string>(sigTime) + boost::lexical_cast<std::string>(stop);

                std::string errorMessage = "";
                if (!obfuScationSigner.VerifyMessage(pmn->pubKeyCoralnode, vchSig, strMessage, errorMessage)) {
                    LogPrint("coralnode", "obseep - Got bad Coralnode address signature %s \n", vin.prevout.hash.ToString());
                    //Misbehaving(pfrom->GetId(), 100);
                    return;
                }

                // fake ping for v11 coralnodes, ignore for v12
                if (pmn->protocolVersion < GETHEADERS_VERSION) pmn->lastPing = CCoralnodePing(vin);
                pmn->nLastDseep = sigTime;
                pmn->Check();
                if (pmn->IsEnabled()) {
                    TRY_LOCK(cs_vNodes, lockNodes);
                    if (!lockNodes) return;
                    LogPrint("coralnode", "obseep - relaying %s \n", vin.prevout.hash.ToString());
                    BOOST_FOREACH (CNode* pnode, vNodes)
                        if (pnode->nVersion >= coralnodePayments.GetMinCoralnodePaymentsProto())
                            pnode->PushMessage("obseep", vin, vchSig, sigTime, stop);
                }
            }
            return;
        }

        LogPrint("coralnode", "obseep - Couldn't find Coralnode entry %s peer=%i\n", vin.prevout.hash.ToString(), pfrom->GetId());

        AskForMN(pfrom, vin);
    }

    /*
     * END OF "REMOVE"
     */
}

void CCoralnodeMan::Remove(CTxIn vin)
{
    LOCK(cs);

    vector<CCoralnode>::iterator it = vCoralnodes.begin();
    while (it != vCoralnodes.end()) {
        if ((*it).vin == vin) {
            LogPrint("coralnode", "CCoralnodeMan: Removing Coralnode %s - %i now\n", (*it).vin.prevout.hash.ToString(), size() - 1);
            vCoralnodes.erase(it);
            break;
        }
        ++it;
    }
}

void CCoralnodeMan::UpdateCoralnodeList(CCoralnodeBroadcast fnb)
{
    LOCK(cs);
    mapSeenCoralnodePing.insert(std::make_pair(fnb.lastPing.GetHash(), fnb.lastPing));
    mapSeenCoralnodeBroadcast.insert(std::make_pair(fnb.GetHash(), fnb));

    LogPrint("coralnode", "CCoralnodeMan::UpdateCoralnodeList -- coralnode=%s\n", fnb.vin.prevout.ToStringShort());

    CCoralnode* pmn = Find(fnb.vin);
    if (pmn == NULL) {
        CCoralnode mn(fnb);
        if (Add(mn)) {
            coralnodeSync.AddedCoralnodeList(fnb.GetHash());
        }
    } else if (pmn->UpdateFromNewBroadcast(fnb)) {
        coralnodeSync.AddedCoralnodeList(fnb.GetHash());
    }
}

std::string CCoralnodeMan::ToString() const
{
    std::ostringstream info;

    info << "Coralnodes: " << (int)vCoralnodes.size() << ", peers who asked us for Coralnode list: " << (int)mAskedUsForCoralnodeList.size() << ", peers we asked for Coralnode list: " << (int)mWeAskedForCoralnodeList.size() << ", entries in Coralnode list we asked for: " << (int)mWeAskedForCoralnodeListEntry.size() << ", nDsqCount: " << (int)nDsqCount;

    return info.str();
}

max_stars_repo_name: Kozisto/caritas

max_issues_repo_name: Kozisto/caritas

max_forks_repo_name: Kozisto/caritas


text:
/**
 * Copyright (c) 2018 Melown Technologies SE
 *
 * 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.
 *
 * 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 HOLDER 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 <boost/lexical_cast.hpp>
#include <boost/utility/in_place_factory.hpp>

#include "jsoncpp/json.hpp"
#include "jsoncpp/as.hpp"

#include "vts-libs/registry/json.hpp"
#include "vts-libs/registry/py.hpp"

#include "../support/python.hpp"
#include "../support/serialization.hpp"

#include "./surface.hpp"

namespace vr = vtslibs::registry;

namespace resource {

constexpr Resource::Generator::Type Surface::type;

bool Surface::Introspection::empty() const
{
    return (tms.empty() && geodata.empty()
            && !position && browserOptions.empty());
}

bool Surface::Introspection::operator!=(const Introspection &other) const
{
    // introspection can safely change
    if (tms != other.tms) { return true; }
    if (geodata != other.geodata) { return true; }
    if (position != other.position) { return true; }

    if (browserOptions.empty() != other.browserOptions.empty()) {
        return true;
    }
    if (!browserOptions.empty()
        && (boost::any_cast<const Json::Value&>(browserOptions)
            != boost::any_cast<const Json::Value&>(other.browserOptions)))
    {
        return true;
    }

    return false;
}

void Surface::parse(const Json::Value &value)
{
    if (value.isMember("nominalTexelSize")) {
        nominalTexelSize = boost::in_place();
        Json::get(*nominalTexelSize, value, "nominalTexelSize");
    }

    if (value.isMember("mergeBottomLod")) {
        mergeBottomLod = boost::in_place();
        Json::get(*mergeBottomLod, value, "mergeBottomLod");
    }

    heightFunction = HeightFunction::parse(value, "heightFunction");

    if (value.isMember("introspection")) {
        const auto &jintrospection(value["introspection"]);

        introspection.tms
            = introspectionListFrom(jintrospection, "tms");
        introspection.geodata
            = introspectionListFrom(jintrospection, "geodata");

        if (jintrospection.isMember("position")) {
            introspection.position
                = vr::positionFromJson(jintrospection["position"]);
        }

        if (jintrospection.isMember("browserOptions")) {
            introspection.browserOptions
                = Json::check(jintrospection["browserOptions"]
                              , Json::objectValue);
        }
    }
}

void Surface::build(Json::Value &value) const
{
    if (nominalTexelSize) {
        value["nominalTexelSize"] = *nominalTexelSize;
    }
    if (mergeBottomLod) {
        value["mergeBottomLod"] = *mergeBottomLod;
    }

    if (heightFunction) {
        boost::any tmp(Json::Value(Json::objectValue));
        heightFunction->build(tmp);
        value["heightFunction"] = boost::any_cast<const Json::Value&>(tmp);
    }

    if (!introspection.empty()) {
        auto &jintrospection(value["introspection"] = Json::objectValue);
        introspectionListTo(jintrospection, "tms", introspection.tms) ;
        introspectionListTo(jintrospection, "geodata", introspection.geodata);

        if (introspection.position) {
            jintrospection["position"] = vr::asJson(*introspection.position);
        }

        if (!introspection.browserOptions.empty()) {
            jintrospection["browserOptions"]
                = boost::any_cast<const Json::Value&>
                (introspection.browserOptions);
        }
    }
}

void Surface::parse(const boost::python::dict &value)
{
    if (value.has_key("nominalTexelSize")) {
        nominalTexelSize
            = boost::python::extract<double>(value["nominalTexelSize"]);
    }

    if (value.has_key("mergeBottomLod")) {
        mergeBottomLod = boost::python::extract
            <vts::Lod>(value["mergeBottomLod"]);
    }

    heightFunction = HeightFunction::parse(value, "heightFunction");

    if (value.has_key("introspection")) {
        boost::python::dict pintrospection(value["introspection"]);
        introspection.tms
            = introspectionListFrom(pintrospection, "tms");
        introspection.geodata
            = introspectionListFrom(pintrospection, "geodata");

        if (pintrospection.has_key("position")) {
            introspection.position = boost::in_place();
            vr::fromPython(*introspection.position
                           , pintrospection["position"]);
        }
    }
}

Changed Surface::changed_impl(const DefinitionBase &o)
    const
{
    const auto &other(o.as<Surface>());

    // manually set data can be changed safely
    if (nominalTexelSize != other.nominalTexelSize) {
        return Changed::safely;
    }

    if (mergeBottomLod != other.mergeBottomLod) {
        return Changed::safely;
    }

    if (introspection != other.introspection) {
        return Changed::safely;
    }

    if (HeightFunction::changed(heightFunction, other.heightFunction)) {
        return Changed::yes;
    }

    return Changed::no;
}

} // namespace resource

max_stars_repo_name: maka-io/vts-mapproxy

max_issues_repo_name: maka-io/vts-mapproxy

max_forks_repo_name: maka-io/vts-mapproxy


text:
///////////////////////////////////////////////////////////////////////////////
//
// Schema.cpp
//
// Copyright (c) 2013-2014 Eric Lombrozo
//
// All Rights Reserved.
//

#include "Schema.h"

#include <stdutils/stringutils.h>

#include <CoinCore/hash.h>
#include <CoinCore/CoinNodeData.h>
#include <CoinCore/MerkleTree.h>
#include <CoinCore/hdkeys.h>
#include <CoinCore/aes.h>
#include <CoinCore/random.h>

#include <logger/logger.h>

// support for boost serialization
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <cstring>

//#define ENABLE_CRYPTO

using namespace CoinDB;

/*
 * class Keychain
 */

Keychain::Keychain(const std::string& name, const secure_bytes_t& entropy, const secure_bytes_t& lock_key)
    : name_(name), hidden_(false)
{
    if (name.empty() || name[0] == '@') throw std::runtime_error("Invalid keychain name.");
    if (entropy.size() < 16) throw std::runtime_error("At least 128 bits of entropy must be supplied.");

    Coin::HDSeed hdSeed(entropy);
    Coin::HDKeychain hdKeychain(hdSeed.getMasterKey(), hdSeed.getMasterChainCode());

    depth_ = (uint32_t)hdKeychain.depth();
    parent_fp_ = hdKeychain.parent_fp();
    child_num_ = hdKeychain.child_num();
    chain_code_ = hdKeychain.chain_code();
    pubkey_ = hdKeychain.pubkey();
    privkey_ = hdKeychain.privkey();
    if (lock_key.empty())
    {
        privkey_salt_ = 0;
        privkey_ciphertext_ = privkey_;
    }
    else
    {
        privkey_salt_ = AES::random_salt();
        privkey_ciphertext_ = AES::encrypt(lock_key, privkey_, true, privkey_salt_);
    }
    privkey_.clear();
    hash_ = hdKeychain.full_hash();
}

Keychain& Keychain::operator=(const Keychain& source)
{
    base::operator=(source);

    depth_ = source.depth_;
    parent_fp_ = source.parent_fp_;
    child_num_ = source.child_num_;
    pubkey_ = source.pubkey_;

    chain_code_ = source.chain_code_;

    privkey_ = source.privkey_;
    privkey_ciphertext_ = source.privkey_ciphertext_;
    privkey_salt_ = source.privkey_salt_;

    parent_ = source.parent_;

    derivation_path_ = source.derivation_path_;

    uchar_vector_secure hashdata = pubkey_;
    hashdata += chain_code_;
    hash_ = ripemd160(sha256(hashdata));

    hidden_ = source.hidden_;

    return *this;
}

std::shared_ptr<Keychain> Keychain::child(uint32_t i, bool get_private, const secure_bytes_t& lock_key)
{
    if (get_private && !isPrivate()) throw std::runtime_error("Cannot get private child from nonprivate keychain.");
    if (get_private)
    {
        if (privkey_.empty()) throw std::runtime_error("Private key is locked.");
        Coin::HDKeychain hdkeychain(privkey_, chain_code_, child_num_, parent_fp_, depth_);
        hdkeychain = hdkeychain.getChild(i);
        std::shared_ptr<Keychain> child(new Keychain());
        child->parent_ = get_shared_ptr();
        child->pubkey_ = hdkeychain.pubkey();
        child->chain_code_ = hdkeychain.chain_code();

        child->privkey_ = hdkeychain.privkey();
        if (lock_key.empty())
        {
            child->privkey_salt_ = 0;
            child->privkey_ciphertext_ = privkey_;
        }
        else
        {
            child->privkey_salt_ = AES::random_salt();
            child->privkey_ciphertext_ = AES::encrypt(lock_key, child->privkey_, true, child->privkey_salt_);
        }
        child->privkey_.clear();

        child->child_num_ = hdkeychain.child_num();
        child->parent_fp_ = hdkeychain.parent_fp();
        child->depth_ = hdkeychain.depth();
        child->hash_ = hdkeychain.full_hash();
        child->derivation_path_ = derivation_path_;
        child->derivation_path_.push_back(i);
        return child;
    }
    else
    {
        Coin::HDKeychain hdkeychain(pubkey_, chain_code_, child_num_, parent_fp_, depth_);
        hdkeychain = hdkeychain.getChild(i);
        std::shared_ptr<Keychain> child(new Keychain());;
        child->parent_ = get_shared_ptr();
        child->pubkey_ = hdkeychain.pubkey();
        child->chain_code_ = hdkeychain.chain_code();
        child->child_num_ = hdkeychain.child_num();
        child->parent_fp_ = hdkeychain.parent_fp();
        child->depth_ = hdkeychain.depth();
        child->hash_ = hdkeychain.full_hash();
        child->derivation_path_ = derivation_path_;
        child->derivation_path_.push_back(i);
        return child;
    }
}

void Keychain::lock() const
{
    privkey_.clear();
}

void Keychain::unlock(const secure_bytes_t& lock_key) const
{
    if (!isPrivate()) throw std::runtime_error("Cannot unlock a nonprivate keychain.");
    if (privkey_salt_ == 0)
    {
        privkey_ = privkey_ciphertext_;
    }
    else
    {
        privkey_ = AES::decrypt(lock_key, privkey_ciphertext_, true, privkey_salt_);
    }
}

bool Keychain::isLocked() const
{
    return privkey_.empty();
}

void Keychain::encrypt(const secure_bytes_t& lock_key)
{
    if (!isPrivate()) throw std::runtime_error("Cannot encrypt a nonprivate keychain.");
    if (isLocked()) throw std::runtime_error("Keychain is locked.");

    privkey_salt_ = AES::random_salt();
    privkey_ciphertext_ = AES::encrypt(lock_key, privkey_, true, privkey_salt_);
}

void Keychain::decrypt()
{
    if (!isPrivate()) throw std::runtime_error("Cannot unencrypt a nonprivate keychain.");
    if (isLocked()) throw std::runtime_error("Keychain is locked.");

    privkey_salt_ = 0;
    privkey_ciphertext_ = privkey_;
}

secure_bytes_t Keychain::getSigningPrivateKey(uint32_t i, const std::vector<uint32_t>& derivation_path) const
{
    if (!isPrivate()) throw std::runtime_error("Missing private key.");
    if (isLocked()) throw std::runtime_error("Private key is locked.");

    // Remove initial zero from privkey if necessary
    secure_bytes_t stripped_privkey = (privkey_.size() > 32) ? secure_bytes_t(privkey_.begin() + 1, privkey_.end()) : privkey_;
    Coin::HDKeychain hdkeychain(stripped_privkey, chain_code_, child_num_, parent_fp_, depth_);
    for (auto k: derivation_path) { hdkeychain = hdkeychain.getChild(k); }
    return hdkeychain.getPrivateSigningKey(i);
}

bytes_t Keychain::getSigningPublicKey(uint32_t i, bool get_compressed, const std::vector<uint32_t>& derivation_path) const
{
    Coin::HDKeychain hdkeychain(pubkey_, chain_code_, child_num_, parent_fp_, depth_);
    for (auto k: derivation_path) { hdkeychain = hdkeychain.getChild(k); }
    return hdkeychain.getPublicSigningKey(i, get_compressed);
}

secure_bytes_t Keychain::privkey() const
{
    if (!isPrivate()) throw std::runtime_error("Keychain is nonprivate.");
    if (privkey_.empty()) throw std::runtime_error("Keychain is locked.");
    return privkey_;
}

void Keychain::importPrivateKey(const Keychain& source)
{
    privkey_ciphertext_ = source.privkey_ciphertext_;
    privkey_salt_ = source.privkey_salt_;
}

void Keychain::importBIP32(const secure_bytes_t& extkey, const secure_bytes_t& lock_key)
{
    Coin::HDKeychain hdKeychain(extkey);

    depth_ = (uint32_t)hdKeychain.depth();
    parent_fp_ = hdKeychain.parent_fp();
    child_num_ = hdKeychain.child_num();
    chain_code_ = hdKeychain.chain_code();
    privkey_.clear();
    if (hdKeychain.isPrivate())
    {
        if (!lock_key.empty())
        {
            privkey_salt_ = AES::random_salt();
            privkey_ciphertext_ = AES::encrypt(lock_key, hdKeychain.key(), true, privkey_salt_);
        }
        else
        {
            privkey_salt_ = 0;
            privkey_ciphertext_ = hdKeychain.key();
        }
    }
    else
    {
        privkey_salt_ = 0;
        privkey_ciphertext_.clear();
    }

    pubkey_ = hdKeychain.pubkey();
    hash_ = hdKeychain.full_hash();
}

secure_bytes_t Keychain::exportBIP32(bool export_private) const
{
    secure_bytes_t key;

    if (export_private)
    {
        if (!isPrivate()) throw std::runtime_error("Keychain is nonprivate.");
        if (isLocked()) throw std::runtime_error("Keychain is locked.");

        // Remove initial zero from privkey if necessary
        key = (privkey_.size() > 32) ? secure_bytes_t(privkey_.begin() + 1, privkey_.end()) : privkey_;
    }
    else
    {
        key = pubkey_;
    }

    return Coin::HDKeychain(key, chain_code_, child_num_, parent_fp_, depth_).extkey();
}

void Keychain::clearPrivateKey()
{
    privkey_.clear();
    privkey_ciphertext_.clear();
    privkey_salt_ = 0;
}

/*
 * class Key
 */

Key::Key(const std::shared_ptr<Keychain>& keychain, uint32_t index, bool compressed)
{
    root_keychain_ = keychain->root();
    derivation_path_ = keychain->derivation_path();
    index_ = index;

    pubkey_ = keychain->getSigningPublicKey(index_, compressed);
    updatePrivate();
}

secure_bytes_t Key::privkey() const
{
    if (!is_private_ || root_keychain_->isLocked()) return secure_bytes_t();
    return root_keychain_->getSigningPrivateKey(index_, derivation_path_);
}

// and a version that throws exceptions
secure_bytes_t Key::try_privkey() const
{
    if (!is_private_) throw std::runtime_error("Key::privkey - cannot get private key from nonprivate key object.");
    if (root_keychain_->isLocked()) throw std::runtime_error("Key::privkey - private key is locked.");

    return root_keychain_->getSigningPrivateKey(index_, derivation_path_);
}

/*
 * class Account
 */

Account::Account(const std::string& name, unsigned int minsigs, const KeychainSet& keychains, uint32_t unused_pool_size, uint32_t time_created, bool compressed_keys)
    : name_(name), minsigs_(minsigs), keychains_(keychains), unused_pool_size_(unused_pool_size), time_created_(time_created), compressed_keys_(compressed_keys)
{
    // TODO: Use exception classes
    if (name_.empty() || name[0] == '@') throw std::runtime_error("Invalid account name.");
    if (keychains_.size() > 15) throw std::runtime_error("Account can use at most 15 keychains.");
    if (minsigs > keychains_.size()) throw std::runtime_error("Account minimum signatures cannot exceed number of keychains.");
    if (minsigs < 1) throw std::runtime_error("Account must require at least one signature.");

    updateHash();
}

void Account::updateHash()
{
    std::vector<bytes_t> keychain_hashes;
    for (auto& keychain: keychains_) { keychain_hashes.push_back(keychain->hash()); }
    std::sort(keychain_hashes.begin(), keychain_hashes.end());

    uchar_vector data;
    data.push_back((unsigned char)minsigs_);
    for (auto& keychain_hash: keychain_hashes) { data += keychain_hash; }

    if (!compressed_keys_) { data.push_back(0x00); }

    hash_ = ripemd160(sha256(data));
}

AccountInfo Account::accountInfo() const
{
    std::vector<std::string> keychain_names;
    for (auto& keychain: keychains_) { keychain_names.push_back(keychain->name()); }

    std::vector<std::string> bin_names;
    uint32_t issued_script_count = 0;
    for (auto& bin: bins_)
    {
        bin_names.push_back(bin->name());
        if (bin->next_script_index() > 0)
        {
            issued_script_count += (bin->next_script_index() - 1);
        }
    }

    return AccountInfo(id_, name_, minsigs_, keychain_names, issued_script_count, unused_pool_size_, time_created_, bin_names, compressed_keys_);
}

std::shared_ptr<AccountBin> Account::addBin(const std::string& name)
{
    uint32_t index = bins_.size() + 1;
    std::shared_ptr<AccountBin> bin(new AccountBin(shared_from_this(), index, name));
    bins_.push_back(bin);
    return bin;
}

/*
 * class AccountBin
 */

AccountBin::AccountBin(std::shared_ptr<Account> account, uint32_t index, const std::string& name)
    : account_(account), index_(index), name_(name), script_count_(0), next_script_index_(0), minsigs_(account->minsigs())
{
    if (index == 0) throw std::runtime_error("Account bin index cannot be zero.");
    if (index == CHANGE_INDEX && name != CHANGE_BIN_NAME) throw std::runtime_error("Account bin index reserved for change.");
    if (index == DEFAULT_INDEX && name != DEFAULT_BIN_NAME) throw std::runtime_error("Account bin index reserved for default.");

    updateHash();
}

std::string AccountBin::account_name() const
{
    return account() ? account()->name() : std::string("@null");
}

SigningScriptVector AccountBin::generateSigningScripts()
{
    SigningScriptVector signingscripts;
    for (uint32_t i = 0; i < next_script_index_; i++)
    {
        std::string label;
        auto it = script_label_map_.find(i);
        if (it != script_label_map_.end())   { label = it->second; }
        SigningScript::status_t status = (index_ == CHANGE_INDEX) ? SigningScript::CHANGE : SigningScript::ISSUED;
        std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), i, label, status));
        signingscripts.push_back(signingscript);
    }

    script_count_ = next_script_index_ + unused_pool_size();
    for (uint32_t i = next_script_index_; i < script_count_; i++)
    {
        std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), i));
        signingscripts.push_back(signingscript);
    }

    return signingscripts;
}

uint32_t AccountBin::unused_pool_size() const
{
    std::shared_ptr<Account> account = account_.lock();
    return account ? account->unused_pool_size() : DEFAULT_UNUSED_POOL_SIZE;
}

void AccountBin::loadKeychains() const
{
    if (!keychains__.empty()) return;
    if (!account())
    {
        // If we do not have an account for this bin we cannot derive the keychains, so use the stored values.
        keychains__ = keychains_;
    }
    else
    {
        for (auto& keychain: account()->keychains())
        {
            std::shared_ptr<Keychain> child(keychain->child(index_));
            keychains__.insert(child);
        }
    }
}

void AccountBin::updateHash()
{
    loadKeychains();

    std::vector<bytes_t> keychain_hashes;
    for (auto& keychain: keychains__) { keychain_hashes.push_back(keychain->hash()); }
    std::sort(keychain_hashes.begin(), keychain_hashes.end());

    uchar_vector data;
    data.push_back((unsigned char)minsigs_);
    for (auto& keychain_hash: keychain_hashes) { data += keychain_hash; }

    hash_ = ripemd160(sha256(data));
}

std::shared_ptr<SigningScript> AccountBin::newSigningScript(const std::string& label)
{
    std::shared_ptr<SigningScript> signingscript(new SigningScript(shared_from_this(), script_count_++, label));
    return signingscript;
}

void AccountBin::markSigningScriptIssued(uint32_t script_index)
{
    if (script_index >= next_script_index_)
        next_script_index_ = script_index + 1;
}

void AccountBin::makeExport(const std::string& name)
{
    name_ = name;
    loadKeychains();
    keychains_ = keychains__;
    for (auto& keychain: keychains_) { keychain->name(""); }
    index_ = 0;
    account_.reset();
}

void AccountBin::makeImport()
{
    updateHash();
    keychains_.clear();
}

void AccountBin::setScriptLabel(uint32_t index, const std::string& label)
{
    if (!label.empty()) { script_label_map_[index] = label; }
}

/*
 * class SigningScript
 */

// static
std::string SigningScript::getStatusString(int status)
{
    std::vector<std::string> flags;
    if (status & UNUSED) flags.push_back("UNUSED");
    if (status & CHANGE) flags.push_back("CHANGE");
    if (status & ISSUED) flags.push_back("ISSUED");
    if (status & USED) flags.push_back("USED");
    if (flags.empty()) return "NONE";

    return stdutils::delimited_list(flags, " | ");
}

// static
std::vector<SigningScript::status_t> SigningScript::getStatusFlags(int status)
{
    std::vector<status_t> flags;
    if (status & UNUSED) flags.push_back(UNUSED);
    if (status & CHANGE) flags.push_back(CHANGE);
    if (status & ISSUED) flags.push_back(ISSUED);
    if (status & USED) flags.push_back(USED);
    return flags;
}

SigningScript::SigningScript(std::shared_ptr<AccountBin> account_bin, uint32_t index, const std::string& label, status_t status)
    : account_(account_bin->account()), account_bin_(account_bin), index_(index), label_(label), status_(status)
{
    if (!account_) throw std::runtime_error("SigningScript::SigningScript() - account is null.");

    auto& keychains = account_bin_->keychains();
    for (auto& keychain: keychains)
    {
        std::shared_ptr<Key> key(new Key(keychain, index, account_->compressed_keys()));
        keys_.push_back(key);
    }

    // sort keys into canonical order
    std::sort(keys_.begin(), keys_.end(), [](std::shared_ptr<Key> key1, std::shared_ptr<Key> key2) { return key1->pubkey() < key2->pubkey(); });

    std::vector<bytes_t> pubkeys;
    for (auto& key: keys_) { pubkeys.push_back(key->pubkey()); }
    CoinQ::Script::Script script(CoinQ::Script::Script::PAY_TO_MULTISIG_SCRIPT_HASH, account_bin->minsigs(), pubkeys);
    txinscript_ = script.txinscript(CoinQ::Script::Script::EDIT);
    txoutscript_ = script.txoutscript();

    account_bin_->setScriptLabel(index, label);
}

void SigningScript::label(const std::string& label)
{
    label_ = label;
    account_bin_->setScriptLabel(index_, label);
}

void SigningScript::status(status_t status)
{
    status_ = status;
    if (status > UNUSED) account_bin_->markSigningScriptIssued(index_);
}

void SigningScript::markUsed()
{
	if (account_bin_->index() == AccountBin::CHANGE_INDEX)
	{
		status_ = CHANGE;
	}
	else
	{
		status_ = USED;
	}

	account_bin_->markSigningScriptIssued(index_);
}

/*
 * class BlockHeader
 */

void BlockHeader::fromCoinCore(const Coin::CoinBlockHeader& blockheader, uint32_t height)
{
    hash_ = blockheader.hash();
    height_ = height;
    version_ = blockheader.version();
    prevhash_ = blockheader.prevBlockHash();
    merkleroot_ = blockheader.merkleRoot();
    timestamp_ = blockheader.timestamp();
    bits_ = blockheader.bits();
    nonce_ = blockheader.nonce();
}

Coin::CoinBlockHeader BlockHeader::toCoinCore() const
{
    return Coin::CoinBlockHeader(version_, timestamp_, bits_, nonce_, prevhash_, merkleroot_);
}

std::string BlockHeader::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"hash\":\"" << uchar_vector(hash_).getHex() << "\","
       << "\"height\":" << height_ << ","
       << "\"version\":" << version_ << ","
       << "\"prevhash\":\"" << uchar_vector(prevhash_).getHex() << "\","
       << "\"merkleroot\":\"" << uchar_vector(merkleroot_).getHex() << "\","
       << "\"timestamp\":" << timestamp_ << ","
       << "\"bits\":" << bits_ << ","
       << "\"nonce\":" << nonce_
       << "}";
    return ss.str();
}

void BlockHeader::updateHash()
{
    hash_ = Coin::CoinBlockHeader(version_, timestamp_, bits_, nonce_, prevhash_, merkleroot_).hash();
}

/*
 * class MerkleBlock
 */

void MerkleBlock::fromCoinCore(const Coin::MerkleBlock& merkleblock, uint32_t height)
{
    blockheader_ = std::shared_ptr<BlockHeader>(new BlockHeader(merkleblock.blockHeader, height));
    txcount_ = merkleblock.nTxs;
    hashes_.assign(merkleblock.hashes.begin(), merkleblock.hashes.end());
    for (auto& hash: hashes_) { std::reverse(hash.begin(), hash.end()); }
    flags_ = merkleblock.flags;
}

Coin::MerkleBlock MerkleBlock::toCoinCore() const
{
    Coin::MerkleBlock merkleblock;
    merkleblock.blockHeader = blockheader_->toCoinCore();
    merkleblock.nTxs = txcount_;
    merkleblock.hashes.assign(hashes_.begin(), hashes_.end());
    for (auto& hash: merkleblock.hashes) { std::reverse(hash.begin(), hash.end()); }
    merkleblock.flags = flags_;
    return merkleblock;
}

std::string MerkleBlock::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"header\":" << blockheader_->toJson() << ","
       << "\"txcount\":" << txcount_ << ","
       << "\"hashes\":[";
    bool addComma = false;
    for (auto& hash: hashes_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << "\"" << uchar_vector(hash).getHex() << "\"";
    }
    ss << "],"
       << "\"flags\":\"" << uchar_vector(flags_).getHex() << "\""
       << "}";
    return ss.str();
}

/*
 * class TxIn
 */

TxIn::TxIn(const Coin::TxIn& coin_txin)
{
	fromCoinCore(coin_txin);
}

TxIn::TxIn(const bytes_t& raw)
{
    Coin::TxIn coin_txin(raw);
	fromCoinCore(coin_txin);
}

void TxIn::fromCoinCore(const Coin::TxIn& coin_txin)
{
    outhash_ = coin_txin.getOutpointHash();
    outindex_ = coin_txin.getOutpointIndex();
    script_ = coin_txin.scriptSig;
    sequence_ = coin_txin.sequence;
}

Coin::TxIn TxIn::toCoinCore() const
{
    Coin::TxIn coin_txin;
    coin_txin.previousOut = Coin::OutPoint(outhash_, outindex_);
    coin_txin.scriptSig = script_;
    coin_txin.sequence = sequence_;
    return coin_txin;
}

bytes_t TxIn::unsigned_script() const
{
    using namespace CoinQ::Script;

    Script script(script_);
    script.clearSigs();
    return script.txinscript(Script::EDIT);
}

bytes_t TxIn::raw() const
{
    return toCoinCore().getSerialized();
}

void TxIn::outpoint(std::shared_ptr<TxOut> outpoint)
{
    outpoint_ = outpoint;
    if (!outpoint) return;

    std::shared_ptr<Tx> tx = outpoint->tx();
    // TODO: Tx exception class
    if (outindex_ != outpoint->txindex())
        throw std::runtime_error("TxIn::outpoint - incorrect outindex.");
    if (!tx)
        throw std::runtime_error("TxIn::outpoint - tx is null.");
    if (tx->status() == Tx::UNSIGNED)
        throw std::runtime_error("TxIn::outpoint - tx is missing signatures.");
    if (outhash_ != tx->hash())
        throw std::runtime_error("TxIn::outpoint - incorrect outhash.");
}

std::string TxIn::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"outhash\":\"" << uchar_vector(outhash_).getHex() << "\","
       << "\"outindex\":" << outindex_ << ","
       << "\"script\":\"" << uchar_vector(script_).getHex() << "\","
       << "\"sequence\":" << sequence_
       << "}";
    return ss.str();
}

/*
 * class TxOut
 */

// static
std::string TxOut::getStatusString(int flags)
{
    std::vector<std::string> str_flags;
    if (flags & UNSPENT)    str_flags.push_back("UNSPENT");
    if (flags & SPENT)      str_flags.push_back("SPENT");
    if (str_flags.empty())  return "NEITHER";
    return stdutils::delimited_list(str_flags, " | ");
}

// static
std::vector<TxOut::status_t> TxOut::getStatusFlags(int flags)
{
    std::vector<status_t> vflags;
    if (flags & UNSPENT)    vflags.push_back(UNSPENT);
    if (flags & SPENT)      vflags.push_back(SPENT);
    return vflags;
}

// static
std::string TxOut::getRoleString(int flags)
{
    std::vector<std::string> str_flags;
    if (flags & ROLE_SENDER)        str_flags.push_back("SEND");
    if (flags & ROLE_RECEIVER)      str_flags.push_back("RECEIVE");
    if (str_flags.empty())          return "NONE";
    return stdutils::delimited_list(str_flags, " | ");
}

// static
std::vector<TxOut::role_t> TxOut::getRoleFlags(int flags)
{
    std::vector<role_t> vflags;
    if (flags & ROLE_SENDER)        vflags.push_back(ROLE_SENDER);
    if (flags & ROLE_RECEIVER)      vflags.push_back(ROLE_RECEIVER);
    return vflags;
}

TxOut::TxOut(uint64_t value, std::shared_ptr<SigningScript> signingscript)
    : value_(value), status_(UNSPENT)
{
    this->signingscript(signingscript);
}

TxOut::TxOut(const Coin::TxOut& coin_txout)
    : value_(coin_txout.value), script_(coin_txout.scriptPubKey), status_(UNSPENT)
{
}

TxOut::TxOut(const bytes_t& raw)
{
    Coin::TxOut coin_txout(raw);
    value_ = coin_txout.value;
    script_ = coin_txout.scriptPubKey;
    status_ = UNSPENT;
}

void TxOut::spent(std::shared_ptr<TxIn> spent)
{
    spent_ = spent;
    status_ = spent ? SPENT : UNSPENT;
}

void TxOut::signingscript(std::shared_ptr<SigningScript> signingscript)
{
    if (!signingscript) throw std::runtime_error("TxOut::signingscript - null signingscript.");

    script_ = signingscript->txoutscript();
    receiving_account_ = signingscript->account();
    if (receiving_label_.empty()) { receiving_label_ = signingscript->label(); }
    account_bin_ = signingscript->account_bin();
    signingscript_ = signingscript;
}

Coin::TxOut TxOut::toCoinCore() const
{
    Coin::TxOut coin_txout;
    coin_txout.value = value_;
    coin_txout.scriptPubKey = script_;
    return coin_txout;
}

bytes_t TxOut::raw() const
{
    return toCoinCore().getSerialized();
}

std::string TxOut::toJson() const
{
    std::stringstream ss;
    ss << "{"
       << "\"value\":" << value_ << ","
       << "\"script\":\"" << uchar_vector(script_).getHex() << "\","
       << "\"sending_label\":\"" << sending_label_ << "\","
       << "\"receiving_label\":\"" << receiving_label_ << "\"";

    if (signingscript_ && signingscript_->contact())
    {
        ss << ",\"sender_username\":\"" << signingscript_->contact()->username() << "\"";
    }

    ss << "}";
    return ss.str();
}

/*
 * class Tx
 */

// static
std::string Tx::getStatusString(int status, bool lowercase)
{
    std::vector<std::string> flags;
    if (status & UNSIGNED) flags.push_back(lowercase ? "unsigned" : "UNSIGNED");
    if (status & UNSENT) flags.push_back(lowercase ? "unsent" : "UNSENT");
    if (status & SENT) flags.push_back(lowercase ? "send" : "SENT");
    if (status & PROPAGATED) flags.push_back(lowercase ? "propagated" : "PROPAGATED");
    if (status & CANCELED) flags.push_back(lowercase ? "canceled" : "CANCELED");
    if (status & CONFIRMED) flags.push_back(lowercase ? "confirmed" : "CONFIRMED");
    if (flags.empty()) return lowercase ? "no_status" : "NO_STATUS";
    return stdutils::delimited_list(flags, " | ");
}

// static
std::vector<Tx::status_t> Tx::getStatusFlags(int status)
{
    std::vector<status_t> flags;
    if (status & UNSIGNED) flags.push_back(UNSIGNED);
    if (status & UNSENT) flags.push_back(UNSENT);
    if (status & SENT) flags.push_back(SENT);
    if (status & PROPAGATED) flags.push_back(PROPAGATED);
    if (status & CANCELED) flags.push_back(CANCELED);
    if (status & CONFIRMED) flags.push_back(CONFIRMED);
    return flags;
}

void Tx::set(uint32_t version, const txins_t& txins, const txouts_t& txouts, uint32_t locktime, uint32_t timestamp, status_t status, bool conflicting)
{
    version_ = version;

    int i = 0;
    txins_.clear();
    for (auto& txin: txins)
    {
        txin->tx(shared_from_this());
        txin->txindex(i++);
        txins_.push_back(txin);
    }

    i = 0;
    txouts_.clear();
    for (auto& txout: txouts)
    {
        txout->tx(shared_from_this());
        txout->txindex(i++);
        txouts_.push_back(txout);
    }

    locktime_ = locktime;
    timestamp_ = timestamp;

    Coin::Transaction coin_tx = toCoinCore();

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

void Tx::set(Coin::Transaction coin_tx, uint32_t timestamp, status_t status, bool conflicting)
{
    //LOGGER(trace) << "Tx::set - fromCoinCore(coin_tx);" << std::endl;
    fromCoinCore(coin_tx);

    timestamp_ = timestamp;

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

void Tx::set(const bytes_t& raw, uint32_t timestamp, status_t status, bool conflicting)
{
    Coin::Transaction coin_tx(raw);
    fromCoinCore(coin_tx);
    timestamp_ = timestamp;

    if (missingSigCount())  { status_ = UNSIGNED; }
    else                    { status_ = status; hash_ = coin_tx.hash(); }

    conflicting_ = conflicting;

    coin_tx.clearScriptSigs();
    unsigned_hash_ = coin_tx.hash();
    updateTotals();
}

bool Tx::updateStatus(status_t status /* = NO_STATUS */)
{
    // Tx is not signed.
    if (missingSigCount())
    {
        status_ = UNSIGNED;
        hash_ = bytes_t();
        return true;
    }

    // Tx status changed from unsigned to signed.
    if (status_ == UNSIGNED)
    {
        switch (status)
        {
        case NO_STATUS:
            status_ = UNSENT;
            break;

        case UNSIGNED:
            status_ = PROPAGATED;
            break;

        default:
            status_ = status;
        }

        hash_ = toCoinCore().hash();
        return true;
    }

    // Only update the status if the new status is valid.
    if (status && status != UNSIGNED && status != status_)
    {
        status_ = status;
        return true;
    }

    return false;
}

Coin::Transaction Tx::toCoinCore() const
{
    Coin::Transaction coin_tx;
    coin_tx.version = version_;
    for (auto& txin:  txins_) { coin_tx.inputs.push_back(txin->toCoinCore()); }
    for (auto& txout: txouts_) { coin_tx.outputs.push_back(txout->toCoinCore()); }
    coin_tx.lockTime = locktime_;
    return coin_tx;
}

void Tx::setBlock(std::shared_ptr<BlockHeader> blockheader, uint32_t blockindex)
{
    blockheader_ = blockheader;
    blockindex_ = blockindex;
}

void Tx::blockheader(std::shared_ptr<BlockHeader> blockheader)
{
    blockheader_ = blockheader;
    if (blockheader)                { status_ = CONFIRMED;  }
    else if (status_ == CONFIRMED)  { status_ = PROPAGATED; }
}

bytes_t Tx::raw() const
{
    return toCoinCore().getSerialized();
}

void Tx::updateTotals()
{
    have_all_outpoints_ = true;
    txin_total_ = 0;
    for (auto& txin: txins_)
    {
        std::shared_ptr<TxOut> outpoint = txin->outpoint();
        if (!outpoint)
        {
            have_all_outpoints_ = false;
        }
        else
        {
            txin_total_ += outpoint->value();
        }
    }

    txout_total_ = 0;
    for (auto& txout: txouts_)
    {
        txout_total_ += txout->value();
    }
}

void Tx::shuffle_txins()
{
    int i = 0;
    std::random_shuffle(txins_.begin(), txins_.end());
    for (auto& txin: txins_) { txin->txindex(i++); }
}

void Tx::shuffle_txouts()
{
    int i = 0;
    std::random_shuffle(txouts_.begin(), txouts_.end());
    for (auto& txout: txouts_) { txout->txindex(i++); }
}

void Tx::fromCoinCore(const Coin::Transaction& coin_tx)
{
    version_ = coin_tx.version;

    int i = 0;
    txins_.clear();
    for (auto& coin_txin: coin_tx.inputs)
    {
        std::shared_ptr<TxIn> txin(new TxIn(coin_txin));
        txin->tx(shared_from_this());
        txin->txindex(i++);
        txins_.push_back(txin);
    }

    i = 0;
    txouts_.clear();
    for (auto& coin_txout: coin_tx.outputs)
    {
        std::shared_ptr<TxOut> txout(new TxOut(coin_txout));
        txout->tx(shared_from_this());
        txout->txindex(i++);
        txouts_.push_back(txout);
    }

    locktime_ = coin_tx.lockTime;
}

unsigned int Tx::missingSigCount() const
{
    // Assume for now all inputs belong to the same account.
    using namespace CoinQ::Script;
    unsigned int count = 0;
    for (auto& txin: txins_)
    {
        Script script(txin->script());
        unsigned int sigsneeded = script.sigsneeded();
        if (sigsneeded > count) count = sigsneeded;
    }
    return count;
}

std::set<bytes_t> Tx::missingSigPubkeys() const
{
    using namespace CoinQ::Script;
    std::set<bytes_t> pubkeys;
    for (auto& txin: txins_)
    {
        Script script(txin->script());
        std::vector<bytes_t> txinpubkeys = script.missingsigs();
        for (auto& txinpubkey: txinpubkeys) { pubkeys.insert(txinpubkey); }
    }
    return pubkeys;
}

std::set<bytes_t> Tx::presentSigPubkeys() const
{
    std::set<bytes_t> pubkeys;

    using namespace CoinQ::Script;
    Signer signer(toCoinCore(), true);
    for (auto& script: signer.getScripts())
    {
        std::vector<bytes_t> txinpubkeys = script.presentsigs();
        for (auto& txinpubkey: txinpubkeys) { pubkeys.insert(txinpubkey); }
    }
    return pubkeys;
}

CoinQ::Script::Signer Tx::signer() const
{
    return CoinQ::Script::Signer(toCoinCore(), true);
}

std::string Tx::toJson(bool includeRawHex) const
{
    std::stringstream ss;
    ss << "{"
       << "\"version\":" << version_ << ","
       << "\"locktime\":" << locktime_ << ","
       << "\"hash\":\"" << uchar_vector(hash()).getHex() << "\","
       << "\"unsignedhash\":\"" << uchar_vector(unsigned_hash()).getHex() << "\","
       << "\"status\":\"" << getStatusString(status_) << "\","
       << "\"height\":";
    if (blockheader_)   { ss << blockheader_->height(); }
    else                { ss << "null"; }
    ss << ","
       << "\"txins\":[";
    bool addComma = false;
    for (auto& txin: txins_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << txin->toJson();
    }
    ss << "],"
       << "\"txouts\":[";
    addComma = false;
    for (auto& txout: txouts_)
    {
        if (addComma)   { ss << ","; }
        else            { addComma = true; }
        ss << txout->toJson();
    }
    ss << "]";

    if (includeRawHex)
    {
        ss << ",\"rawhex\":\"" << uchar_vector(raw()).getHex() << "\"";
    }

    ss << "}";
    return ss.str();
}

std::string Tx::toSerialized() const
{
    std::stringstream ss;
    boost::archive::text_oarchive oa(ss);
    oa << *this;
    return ss.str();
}

void Tx::fromSerialized(const std::string& serialized)
{
    std::stringstream ss;
    ss << serialized;
    boost::archive::text_iarchive ia(ss);
    ia >> *this;
}

max_stars_repo_name: anypath/CoinVault

max_stars_repo_licenses: Unlicense OpenSSL MIT

max_issues_repo_name: anypath/CoinVault

max_issues_repo_licenses: Unlicense OpenSSL MIT

max_forks_repo_name: anypath/CoinVault

max_forks_repo_licenses: Unlicense OpenSSL MIT