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

#include "quorums.h"
#include "quorums_blockprocessor.h"
#include "quorums_commitment.h"
#include "quorums_dkgsession.h"
#include "quorums_dkgsessionmgr.h"
#include "quorums_init.h"
#include "quorums_utils.h"

#include "evo/specialtx.h"

#include "activemasternode.h"
#include "chainparams.h"
#include "init.h"
#include "univalue.h"
#include "validation.h"

#include "cxxtimer.hpp"

namespace llmq
{

static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";

CQuorumManager* quorumManager;

static uint256 MakeQuorumKey(const CQuorum& q)
{
    CHashWriter hw(SER_NETWORK, 0);
    hw << (uint8_t)q.params.type;
    hw << q.quorumHash;
    for (const auto& dmn : q.members) {
        hw << dmn->proTxHash;
    }
    return hw.GetHash();
}

CQuorum::~CQuorum()
{
    // most likely the thread is already done
    stopCachePopulatorThread = true;
    if (cachePopulatorThread.joinable()) {
        cachePopulatorThread.join();
    }
}

void CQuorum::Init(const uint256& _quorumHash, int _height, const std::vector<CDeterministicMNCPtr>& _members, const std::vector<bool>& _validMembers, const CBLSPublicKey& _quorumPublicKey)
{
    quorumHash = _quorumHash;
    height = _height;
    members = _members;
    validMembers = _validMembers;
    quorumPublicKey = _quorumPublicKey;
}

bool CQuorum::IsMember(const uint256& proTxHash) const
{
    for (auto& dmn : members) {
        if (dmn->proTxHash == proTxHash) {
            return true;
        }
    }
    return false;
}

bool CQuorum::IsValidMember(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return validMembers[i];
        }
    }
    return false;
}

CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
{
    if (quorumVvec == nullptr || memberIdx >= members.size() || !validMembers[memberIdx]) {
        return CBLSPublicKey();
    }
    auto& m = members[memberIdx];
    return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
}

CBLSSecretKey CQuorum::GetSkShare() const
{
    return skShare;
}

int CQuorum::GetMemberIndex(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return (int)i;
        }
    }
    return -1;
}

bool CQuorum::WriteContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    if (quorumVvec != nullptr) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
    }
    if (skShare.IsValid()) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
    }
    return true;
}

bool CQuorum::ReadContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    BLSVerificationVector qv;
    if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
        quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
    } else {
        return false;
    }

    // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
    // member of the quorum but observed the whole DKG process to have the quorum verification vector.
    evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);

    return true;
}

void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
{
    if (_this->quorumVvec == nullptr) {
        return;
    }

    cxxtimer::Timer t(true);
    LogPrintf("CQuorum::StartCachePopulatorThread -- start\n");

    // this thread will exit after some time
    // when then later some other thread tries to get keys, it will be much faster
    _this->cachePopulatorThread = std::thread([_this, t]() {
        RenameThread("quorum-cachepop");
        for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
            if (_this->validMembers[i]) {
                _this->GetPubKeyShare(i);
            }
        }
        LogPrintf("CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
    });
}

CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
    evoDb(_evoDb),
    blsWorker(_blsWorker),
    dkgManager(_dkgManager)
{
}

void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
{
    if (fInitialDownload) {
        return;
    }

    LOCK(cs_main);

    for (auto& p : Params().GetConsensus().llmqs) {
        EnsureQuorumConnections(p.first, pindexNew);
    }
}

void CQuorumManager::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew)
{
    AssertLockHeld(cs_main);

    const auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto myProTxHash = activeMasternodeInfo.proTxHash;
    auto lastQuorums = ScanQuorums(llmqType, (size_t)params.keepOldConnections);

    auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);

    // don't remove connections for the currently in-progress DKG round
    int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
    auto curDkgBlock = chainActive[curDkgHeight]->GetBlockHash();
    connmanQuorumsToDelete.erase(curDkgBlock);

    for (auto& quorum : lastQuorums) {
        if (!quorum->IsMember(myProTxHash) && !GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS)) {
            continue;
        }

        if (!g_connman->HasMasternodeQuorumNodes(llmqType, quorum->quorumHash)) {
            std::set<CService> connections;
            if (quorum->IsMember(myProTxHash)) {
                connections = CLLMQUtils::GetQuorumConnections(llmqType, quorum->quorumHash, myProTxHash);
            } else {
                auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, quorum->quorumHash, quorum->members.size(), 1);
                for (auto idx : cindexes) {
                    connections.emplace(quorum->members[idx]->pdmnState->addr);
                }
            }
            if (!connections.empty()) {
                std::string debugMsg = strprintf("CQuorumManager::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, quorum->quorumHash.ToString());
                for (auto& c : connections) {
                    debugMsg += strprintf("  %s\n", c.ToString(false));
                }
                LogPrintf(debugMsg);
                g_connman->AddMasternodeQuorumNodes(llmqType, quorum->quorumHash, connections);
            }
        }
        connmanQuorumsToDelete.erase(quorum->quorumHash);
    }

    for (auto& qh : connmanQuorumsToDelete) {
        LogPrintf("CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
        g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
    }
}

bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, std::shared_ptr<CQuorum>& quorum) const
{
    AssertLockHeld(cs_main);

    if (!mapBlockIndex.count(qc.quorumHash)) {
        LogPrintf("CQuorumManager::%s -- block %s not found", __func__, qc.quorumHash.ToString());
        return false;
    }
    auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType);
    auto quorumIndex = mapBlockIndex[qc.quorumHash];
    auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash);

    quorum->Init(qc.quorumHash, quorumIndex->nHeight, members, qc.validMembers, qc.quorumPublicKey);

    if (!quorum->ReadContributions(evoDb)) {
        if (BuildQuorumContributions(qc, quorum)) {
            quorum->WriteContributions(evoDb);
        } else {
            LogPrintf("CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed", __func__, qc.quorumHash.ToString());
        }
    }

    // pre-populate caches in the background
    // recovering public key shares is quite expensive and would result in serious lags for the first few signing
    // sessions if the shares would be calculated on-demand
    CQuorum::StartCachePopulatorThread(quorum);

    return true;
}

bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
{
    std::vector<uint16_t> memberIndexes;
    std::vector<BLSVerificationVectorPtr> vvecs;
    BLSSecretKeyVector skContributions;
    if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
        return false;
    }

    BLSVerificationVectorPtr quorumVvec;
    CBLSSecretKey skShare;

    cxxtimer::Timer t2(true);
    quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
    if (quorumVvec == nullptr) {
        LogPrintf("CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
        // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
        // allows to use the quorum as a non-member (verification through the quorum pub key)
        return false;
    }
    skShare = blsWorker.AggregateSecretKeys(skContributions);
    if (!skShare.IsValid()) {
        LogPrintf("CQuorumManager::%s -- failed to build skShare\n", __func__);
        // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
        // have a valid quorum vvec at this point)
    }
    t2.stop();

    LogPrintf("CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());

    quorum->quorumVvec = quorumVvec;
    quorum->skShare = skShare;

    return true;
}

bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
{
    LOCK(cs_main);
    return ScanQuorums(llmqType, chainActive.Tip()->GetBlockHash(), maxCount);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const uint256& startBlock, size_t maxCount)
{
    std::vector<CQuorumCPtr> result;

    LOCK(cs_main);
    if (!mapBlockIndex.count(startBlock)) {
        return result;
    }

    result.reserve(maxCount);

    CBlockIndex* pindex = mapBlockIndex[startBlock];

    while (pindex != NULL && result.size() < maxCount && deterministicMNManager->IsDIP3Active(pindex->nHeight)) {
        if (HasQuorum(llmqType, pindex->GetBlockHash())) {
            auto quorum = GetQuorum(llmqType, pindex->GetBlockHash());
            if (quorum) {
                result.emplace_back(quorum);
            }
        }

        // TODO speedup (skip blocks where no quorums could have been mined)
        pindex = pindex->pprev;
    }

    return result;
}

CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& selectionHash, size_t poolSize)
{
    LOCK(cs_main);
    return SelectQuorum(llmqType, chainActive.Tip()->GetBlockHash(), selectionHash, poolSize);
}

CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& startBlock, const uint256& selectionHash, size_t poolSize)
{
    auto quorums = ScanQuorums(llmqType, startBlock, poolSize);
    if (quorums.empty()) {
        return nullptr;
    }

    std::vector<std::pair<uint256, size_t>> scores;
    scores.reserve(quorums.size());
    for (size_t i = 0; i < quorums.size(); i++) {
        CHashWriter h(SER_NETWORK, 0);
        h << (uint8_t)llmqType;
        h << quorums[i]->quorumHash;
        h << selectionHash;
        scores.emplace_back(h.GetHash(), i);
    }
    std::sort(scores.begin(), scores.end());
    return quorums[scores.front().second];
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    AssertLockHeld(cs_main);

    // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
    // cached quorums which are not in the active chain anymore
    if (!HasQuorum(llmqType, quorumHash)) {
        return nullptr;
    }

    LOCK(quorumsCacheCs);

    auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
    if (it != quorumsCache.end()) {
        return it->second;
    }

    CFinalCommitment qc;
    if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc)) {
        return nullptr;
    }

    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto quorum = std::make_shared<CQuorum>(params, blsWorker);
    if (!BuildQuorumFromCommitment(qc, quorum)) {
        return nullptr;
    }

    quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);

    return quorum;
}

CQuorumCPtr CQuorumManager::GetNewestQuorum(Consensus::LLMQType llmqType)
{
    auto quorums = ScanQuorums(llmqType, 1);
    if (quorums.empty()) {
        return nullptr;
    }
    return quorums.front();
}

}
<commit_msg>Skip starting of cache populator thread in case we don't have a valid vvec<commit_after>// Copyright (c) 2018 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums.h"
#include "quorums_blockprocessor.h"
#include "quorums_commitment.h"
#include "quorums_dkgsession.h"
#include "quorums_dkgsessionmgr.h"
#include "quorums_init.h"
#include "quorums_utils.h"

#include "evo/specialtx.h"

#include "activemasternode.h"
#include "chainparams.h"
#include "init.h"
#include "univalue.h"
#include "validation.h"

#include "cxxtimer.hpp"

namespace llmq
{

static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";

CQuorumManager* quorumManager;

static uint256 MakeQuorumKey(const CQuorum& q)
{
    CHashWriter hw(SER_NETWORK, 0);
    hw << (uint8_t)q.params.type;
    hw << q.quorumHash;
    for (const auto& dmn : q.members) {
        hw << dmn->proTxHash;
    }
    return hw.GetHash();
}

CQuorum::~CQuorum()
{
    // most likely the thread is already done
    stopCachePopulatorThread = true;
    if (cachePopulatorThread.joinable()) {
        cachePopulatorThread.join();
    }
}

void CQuorum::Init(const uint256& _quorumHash, int _height, const std::vector<CDeterministicMNCPtr>& _members, const std::vector<bool>& _validMembers, const CBLSPublicKey& _quorumPublicKey)
{
    quorumHash = _quorumHash;
    height = _height;
    members = _members;
    validMembers = _validMembers;
    quorumPublicKey = _quorumPublicKey;
}

bool CQuorum::IsMember(const uint256& proTxHash) const
{
    for (auto& dmn : members) {
        if (dmn->proTxHash == proTxHash) {
            return true;
        }
    }
    return false;
}

bool CQuorum::IsValidMember(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return validMembers[i];
        }
    }
    return false;
}

CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
{
    if (quorumVvec == nullptr || memberIdx >= members.size() || !validMembers[memberIdx]) {
        return CBLSPublicKey();
    }
    auto& m = members[memberIdx];
    return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
}

CBLSSecretKey CQuorum::GetSkShare() const
{
    return skShare;
}

int CQuorum::GetMemberIndex(const uint256& proTxHash) const
{
    for (size_t i = 0; i < members.size(); i++) {
        if (members[i]->proTxHash == proTxHash) {
            return (int)i;
        }
    }
    return -1;
}

bool CQuorum::WriteContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    if (quorumVvec != nullptr) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
    }
    if (skShare.IsValid()) {
        evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
    }
    return true;
}

bool CQuorum::ReadContributions(CEvoDB& evoDb)
{
    uint256 dbKey = MakeQuorumKey(*this);

    BLSVerificationVector qv;
    if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
        quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
    } else {
        return false;
    }

    // We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
    // member of the quorum but observed the whole DKG process to have the quorum verification vector.
    evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);

    return true;
}

void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
{
    if (_this->quorumVvec == nullptr) {
        return;
    }

    cxxtimer::Timer t(true);
    LogPrintf("CQuorum::StartCachePopulatorThread -- start\n");

    // this thread will exit after some time
    // when then later some other thread tries to get keys, it will be much faster
    _this->cachePopulatorThread = std::thread([_this, t]() {
        RenameThread("quorum-cachepop");
        for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
            if (_this->validMembers[i]) {
                _this->GetPubKeyShare(i);
            }
        }
        LogPrintf("CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
    });
}

CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
    evoDb(_evoDb),
    blsWorker(_blsWorker),
    dkgManager(_dkgManager)
{
}

void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
{
    if (fInitialDownload) {
        return;
    }

    LOCK(cs_main);

    for (auto& p : Params().GetConsensus().llmqs) {
        EnsureQuorumConnections(p.first, pindexNew);
    }
}

void CQuorumManager::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew)
{
    AssertLockHeld(cs_main);

    const auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto myProTxHash = activeMasternodeInfo.proTxHash;
    auto lastQuorums = ScanQuorums(llmqType, (size_t)params.keepOldConnections);

    auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);

    // don't remove connections for the currently in-progress DKG round
    int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
    auto curDkgBlock = chainActive[curDkgHeight]->GetBlockHash();
    connmanQuorumsToDelete.erase(curDkgBlock);

    for (auto& quorum : lastQuorums) {
        if (!quorum->IsMember(myProTxHash) && !GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS)) {
            continue;
        }

        if (!g_connman->HasMasternodeQuorumNodes(llmqType, quorum->quorumHash)) {
            std::set<CService> connections;
            if (quorum->IsMember(myProTxHash)) {
                connections = CLLMQUtils::GetQuorumConnections(llmqType, quorum->quorumHash, myProTxHash);
            } else {
                auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, quorum->quorumHash, quorum->members.size(), 1);
                for (auto idx : cindexes) {
                    connections.emplace(quorum->members[idx]->pdmnState->addr);
                }
            }
            if (!connections.empty()) {
                std::string debugMsg = strprintf("CQuorumManager::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, quorum->quorumHash.ToString());
                for (auto& c : connections) {
                    debugMsg += strprintf("  %s\n", c.ToString(false));
                }
                LogPrintf(debugMsg);
                g_connman->AddMasternodeQuorumNodes(llmqType, quorum->quorumHash, connections);
            }
        }
        connmanQuorumsToDelete.erase(quorum->quorumHash);
    }

    for (auto& qh : connmanQuorumsToDelete) {
        LogPrintf("CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
        g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
    }
}

bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, std::shared_ptr<CQuorum>& quorum) const
{
    AssertLockHeld(cs_main);

    if (!mapBlockIndex.count(qc.quorumHash)) {
        LogPrintf("CQuorumManager::%s -- block %s not found", __func__, qc.quorumHash.ToString());
        return false;
    }
    auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType);
    auto quorumIndex = mapBlockIndex[qc.quorumHash];
    auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash);

    quorum->Init(qc.quorumHash, quorumIndex->nHeight, members, qc.validMembers, qc.quorumPublicKey);

    bool hasValidVvec = false;
    if (quorum->ReadContributions(evoDb)) {
        hasValidVvec = true;
    } else {
        if (BuildQuorumContributions(qc, quorum)) {
            quorum->WriteContributions(evoDb);
            hasValidVvec = true;
        } else {
            LogPrintf("CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed", __func__, qc.quorumHash.ToString());
        }
    }

    if (hasValidVvec) {
        // pre-populate caches in the background
        // recovering public key shares is quite expensive and would result in serious lags for the first few signing
        // sessions if the shares would be calculated on-demand
        CQuorum::StartCachePopulatorThread(quorum);
    }

    return true;
}

bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
{
    std::vector<uint16_t> memberIndexes;
    std::vector<BLSVerificationVectorPtr> vvecs;
    BLSSecretKeyVector skContributions;
    if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
        return false;
    }

    BLSVerificationVectorPtr quorumVvec;
    CBLSSecretKey skShare;

    cxxtimer::Timer t2(true);
    quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
    if (quorumVvec == nullptr) {
        LogPrintf("CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
        // without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
        // allows to use the quorum as a non-member (verification through the quorum pub key)
        return false;
    }
    skShare = blsWorker.AggregateSecretKeys(skContributions);
    if (!skShare.IsValid()) {
        LogPrintf("CQuorumManager::%s -- failed to build skShare\n", __func__);
        // We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
        // have a valid quorum vvec at this point)
    }
    t2.stop();

    LogPrintf("CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());

    quorum->quorumVvec = quorumVvec;
    quorum->skShare = skShare;

    return true;
}

bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
{
    LOCK(cs_main);
    return ScanQuorums(llmqType, chainActive.Tip()->GetBlockHash(), maxCount);
}

std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const uint256& startBlock, size_t maxCount)
{
    std::vector<CQuorumCPtr> result;

    LOCK(cs_main);
    if (!mapBlockIndex.count(startBlock)) {
        return result;
    }

    result.reserve(maxCount);

    CBlockIndex* pindex = mapBlockIndex[startBlock];

    while (pindex != NULL && result.size() < maxCount && deterministicMNManager->IsDIP3Active(pindex->nHeight)) {
        if (HasQuorum(llmqType, pindex->GetBlockHash())) {
            auto quorum = GetQuorum(llmqType, pindex->GetBlockHash());
            if (quorum) {
                result.emplace_back(quorum);
            }
        }

        // TODO speedup (skip blocks where no quorums could have been mined)
        pindex = pindex->pprev;
    }

    return result;
}

CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& selectionHash, size_t poolSize)
{
    LOCK(cs_main);
    return SelectQuorum(llmqType, chainActive.Tip()->GetBlockHash(), selectionHash, poolSize);
}

CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& startBlock, const uint256& selectionHash, size_t poolSize)
{
    auto quorums = ScanQuorums(llmqType, startBlock, poolSize);
    if (quorums.empty()) {
        return nullptr;
    }

    std::vector<std::pair<uint256, size_t>> scores;
    scores.reserve(quorums.size());
    for (size_t i = 0; i < quorums.size(); i++) {
        CHashWriter h(SER_NETWORK, 0);
        h << (uint8_t)llmqType;
        h << quorums[i]->quorumHash;
        h << selectionHash;
        scores.emplace_back(h.GetHash(), i);
    }
    std::sort(scores.begin(), scores.end());
    return quorums[scores.front().second];
}

CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
{
    AssertLockHeld(cs_main);

    // we must check this before we look into the cache. Reorgs might have happened which would mean we might have
    // cached quorums which are not in the active chain anymore
    if (!HasQuorum(llmqType, quorumHash)) {
        return nullptr;
    }

    LOCK(quorumsCacheCs);

    auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
    if (it != quorumsCache.end()) {
        return it->second;
    }

    CFinalCommitment qc;
    if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc)) {
        return nullptr;
    }

    auto& params = Params().GetConsensus().llmqs.at(llmqType);

    auto quorum = std::make_shared<CQuorum>(params, blsWorker);
    if (!BuildQuorumFromCommitment(qc, quorum)) {
        return nullptr;
    }

    quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);

    return quorum;
}

CQuorumCPtr CQuorumManager::GetNewestQuorum(Consensus::LLMQType llmqType)
{
    auto quorums = ScanQuorums(llmqType, 1);
    if (quorums.empty()) {
        return nullptr;
    }
    return quorums.front();
}

}
<|endoftext|>


<commit_before>#include "openmc/source.h"

#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define HAS_DYNAMIC_LINKING
#endif

#include <algorithm> // for move
#include <memory> // for unique_ptr

#ifdef HAS_DYNAMIC_LINKING
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
#endif

#include <fmt/core.h>
#include "xtensor/xadapt.hpp"

#include "openmc/bank.h"
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/material.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
#include "openmc/nuclide.h"
#include "openmc/capi.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/state_point.h"
#include "openmc/xml_interface.h"

namespace openmc {

//==============================================================================
// Global variables
//==============================================================================

namespace model {

std::vector<std::unique_ptr<Source>> external_sources;

}

//==============================================================================
// IndependentSource implementation
//==============================================================================

IndependentSource::IndependentSource(UPtrSpace space, UPtrAngle angle, UPtrDist energy)
  : space_{std::move(space)}, angle_{std::move(angle)}, energy_{std::move(energy)} { }

IndependentSource::IndependentSource(pugi::xml_node node)
{
  // Check for particle type
  if (check_for_node(node, "particle")) {
    auto temp_str = get_node_value(node, "particle", true, true);
    if (temp_str == "neutron") {
      particle_ = Particle::Type::neutron;
    } else if (temp_str == "photon") {
      particle_ = Particle::Type::photon;
      settings::photon_transport = true;
    } else {
      fatal_error(std::string("Unknown source particle type: ") + temp_str);
    }
  }

  // Check for source strength
  if (check_for_node(node, "strength")) {
    strength_ = std::stod(get_node_value(node, "strength"));
  }

  // Check for external source file
  if (check_for_node(node, "file")) {
    // Copy path of source file
    settings::path_source = get_node_value(node, "file", false, true);

    // Check if source file exists
    if (!file_exists(settings::path_source)) {
      fatal_error(fmt::format("Source file '{}' does not exist.",
        settings::path_source));
    }

  } else if (check_for_node(node, "library")) {
    settings::path_source_library = get_node_value(node, "library", false, true);
    if (!file_exists(settings::path_source_library)) {
      fatal_error(fmt::format("Source library '{}' does not exist.",
        settings::path_source_library));
    }

  } else {

    // Spatial distribution for external source
    if (check_for_node(node, "space")) {
      // Get pointer to spatial distribution
      pugi::xml_node node_space = node.child("space");

      // Check for type of spatial distribution and read
      std::string type;
      if (check_for_node(node_space, "type"))
        type = get_node_value(node_space, "type", true, true);
      if (type == "cartesian") {
        space_ = UPtrSpace{new CartesianIndependent(node_space)};
      } else if (type == "cylindrical") {
        space_ = UPtrSpace{new CylindricalIndependent(node_space)};
      } else if (type == "spherical") {
        space_ = UPtrSpace{new SphericalIndependent(node_space)};
      } else if (type == "box") {
        space_ = UPtrSpace{new SpatialBox(node_space)};
      } else if (type == "fission") {
        space_ = UPtrSpace{new SpatialBox(node_space, true)};
      } else if (type == "point") {
        space_ = UPtrSpace{new SpatialPoint(node_space)};
      } else {
        fatal_error(fmt::format(
          "Invalid spatial distribution for external source: {}", type));
      }

    } else {
      // If no spatial distribution specified, make it a point source
      space_ = UPtrSpace{new SpatialPoint()};
    }

    // Determine external source angular distribution
    if (check_for_node(node, "angle")) {
      // Get pointer to angular distribution
      pugi::xml_node node_angle = node.child("angle");

      // Check for type of angular distribution
      std::string type;
      if (check_for_node(node_angle, "type"))
        type = get_node_value(node_angle, "type", true, true);
      if (type == "isotropic") {
        angle_ = UPtrAngle{new Isotropic()};
      } else if (type == "monodirectional") {
        angle_ = UPtrAngle{new Monodirectional(node_angle)};
      } else if (type == "mu-phi") {
        angle_ = UPtrAngle{new PolarAzimuthal(node_angle)};
      } else {
        fatal_error(fmt::format(
          "Invalid angular distribution for external source: {}", type));
      }

    } else {
      angle_ = UPtrAngle{new Isotropic()};
    }

    // Determine external source energy distribution
    if (check_for_node(node, "energy")) {
      pugi::xml_node node_dist = node.child("energy");
      energy_ = distribution_from_xml(node_dist);
    } else {
      // Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
      energy_ = UPtrDist{new Watt(0.988e6, 2.249e-6)};
    }
  }
}

Particle::Bank IndependentSource::sample(uint64_t* seed) const
{
  Particle::Bank site;

  // Set weight to one by default
  site.wgt = 1.0;

  // Repeat sampling source location until a good site has been found
  bool found = false;
  int n_reject = 0;
  static int n_accept = 0;
  while (!found) {
    // Set particle type
    site.particle = particle_;

    // Sample spatial distribution
    site.r = space_->sample(seed);

    // Now search to see if location exists in geometry
    int32_t cell_index, instance;
    double xyz[] {site.r.x, site.r.y, site.r.z};
    int err = openmc_find_cell(xyz, &cell_index, &instance);
    found = (err != OPENMC_E_GEOMETRY);

    // Check if spatial site is in fissionable material
    if (found) {
      auto space_box = dynamic_cast<SpatialBox*>(space_.get());
      if (space_box) {
        if (space_box->only_fissionable()) {
          // Determine material
          const auto& c = model::cells[cell_index];
          auto mat_index = c->material_.size() == 1
            ? c->material_[0] : c->material_[instance];

          if (mat_index == MATERIAL_VOID) {
            found = false;
          } else {
            if (!model::materials[mat_index]->fissionable_) found = false;
          }
        }
      }
    }

    // Check for rejection
    if (!found) {
      ++n_reject;
      if (n_reject >= EXTSRC_REJECT_THRESHOLD &&
          static_cast<double>(n_accept)/n_reject <= EXTSRC_REJECT_FRACTION) {
        fatal_error("More than 95% of external source sites sampled were "
                    "rejected. Please check your external source definition.");
      }
    }
  }

  // Increment number of accepted samples
  ++n_accept;

  // Sample angle
  site.u = angle_->sample(seed);

  // Check for monoenergetic source above maximum particle energy
  auto p = static_cast<int>(particle_);
  auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
  if (energy_ptr) {
    auto energies = xt::adapt(energy_ptr->x());
    if (xt::any(energies > data::energy_max[p])) {
      fatal_error("Source energy above range of energies of at least "
                  "one cross section table");
    } else if (xt::any(energies < data::energy_min[p])) {
      fatal_error("Source energy below range of energies of at least "
                  "one cross section table");
    }
  }

  while (true) {
    // Sample energy spectrum
    site.E = energy_->sample(seed);

    // Resample if energy falls outside minimum or maximum particle energy
    if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
  }

  // Set delayed group
  site.delayed_group = 0;

  return site;
}

//==============================================================================
// FileSource implementation
//==============================================================================

FileSource::FileSource(std::string path)
{
  // Check if source file exists
  if (!file_exists(path)) {
    fatal_error(fmt::format("Source file '{}' does not exist.", path));
  }

  // Read the source from a binary file instead of sampling from some
  // assumed source distribution
  write_message(6, "Reading source file from {}...", path);

  // Open the binary file
  hid_t file_id = file_open(path, 'r', true);

  // Check to make sure this is a source file
  std::string filetype;
  read_attribute(file_id, "filetype", filetype);
  if (filetype != "source" && filetype != "statepoint") {
    fatal_error("Specified starting source file not a source file type.");
  }

  // Read in the source particles
  read_source_bank(file_id, sites_, false);

  // Close file
  file_close(file_id);
}

Particle::Bank FileSource::sample(uint64_t* seed) const
{
  size_t i_site = sites_.size()*prn(seed);
  return sites_[i_site];
}

//==============================================================================
// CustomSourceWrapper implementation
//==============================================================================

CustomSourceWrapper::CustomSourceWrapper(std::string path, std::string parameters)
{
#ifdef HAS_DYNAMIC_LINKING
  // Open the library
  shared_library_ = dlopen(path.c_str(), RTLD_LAZY);
  if (!shared_library_) {
    fatal_error("Couldn't open source library " + path);
  }

  // reset errors
  dlerror();

  // get the function to create the custom source from the library
  auto create_custom_source = reinterpret_cast<create_custom_source_t*>(
    dlsym(shared_library_, "openmc_create_source"));

  // check for any dlsym errors
  auto dlsym_error = dlerror();
  if (dlsym_error) {
    std::string error_msg = fmt::format("Couldn't open the openmc_create_source symbol: {}", dlsym_error);
    dlclose(shared_library_);
    fatal_error(error_msg);
  }

  // create a pointer to an instance of the custom source
  custom_source_ = create_custom_source(parameters);

#else
  fatal_error("Custom source libraries have not yet been implemented for "
    "non-POSIX systems");
#endif
}

CustomSourceWrapper::~CustomSourceWrapper()
{
  // Make sure custom source is cleared before closing shared library
  if (custom_source_.get()) custom_source_.reset();

#ifdef HAS_DYNAMIC_LINKING
  dlclose(shared_library_);
#else
  fatal_error("Custom source libraries have not yet been implemented for "
              "non-POSIX systems");
#endif
}

//==============================================================================
// Non-member functions
//==============================================================================

void initialize_source()
{
  write_message("Initializing source particles...", 5);

  // Generation source sites from specified distribution in user input
  #pragma omp parallel for
  for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
    // initialize random number seed
    int64_t id = simulation::total_gen*settings::n_particles +
      simulation::work_index[mpi::rank] + i + 1;
    uint64_t seed = init_seed(id, STREAM_SOURCE);

    // sample external source distribution
    simulation::source_bank[i] = sample_external_source(&seed);
  }

  // Write out initial source
  if (settings::write_initial_source) {
    write_message("Writing out initial source...", 5);
    std::string filename = settings::path_output + "initial_source.h5";
    hid_t file_id = file_open(filename, 'w', true);
    write_source_bank(file_id, false);
    file_close(file_id);
  }
}

Particle::Bank sample_external_source(uint64_t* seed)
{
  // Determine total source strength
  double total_strength = 0.0;
  for (auto& s : model::external_sources)
    total_strength += s->strength();

  // Sample from among multiple source distributions
  int i = 0;
  if (model::external_sources.size() > 1) {
    double xi = prn(seed)*total_strength;
    double c = 0.0;
    for (; i < model::external_sources.size(); ++i) {
      c += model::external_sources[i]->strength();
      if (xi < c) break;
    }
  }

  // Sample source site from i-th source distribution
  Particle::Bank site {model::external_sources[i]->sample(seed)};

  // If running in MG, convert site.E to group
  if (!settings::run_CE) {
    site.E = lower_bound_index(data::mg.rev_energy_bins_.begin(),
      data::mg.rev_energy_bins_.end(), site.E);
    site.E = data::mg.num_energy_groups_ - site.E - 1.;
  }

  return site;
}

void free_memory_source()
{
  model::external_sources.clear();
}

} // namespace openmc
<commit_msg>Resolve rebase leftover<commit_after>#include "openmc/source.h"

#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#define HAS_DYNAMIC_LINKING
#endif

#include <algorithm> // for move
#include <memory> // for unique_ptr

#ifdef HAS_DYNAMIC_LINKING
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
#endif

#include <fmt/core.h>
#include "xtensor/xadapt.hpp"

#include "openmc/bank.h"
#include "openmc/cell.h"
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/material.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
#include "openmc/nuclide.h"
#include "openmc/capi.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/state_point.h"
#include "openmc/xml_interface.h"

namespace openmc {

//==============================================================================
// Global variables
//==============================================================================

namespace model {

std::vector<std::unique_ptr<Source>> external_sources;

}

//==============================================================================
// IndependentSource implementation
//==============================================================================

IndependentSource::IndependentSource(UPtrSpace space, UPtrAngle angle, UPtrDist energy)
  : space_{std::move(space)}, angle_{std::move(angle)}, energy_{std::move(energy)} { }

IndependentSource::IndependentSource(pugi::xml_node node)
{
  // Check for particle type
  if (check_for_node(node, "particle")) {
    auto temp_str = get_node_value(node, "particle", true, true);
    if (temp_str == "neutron") {
      particle_ = Particle::Type::neutron;
    } else if (temp_str == "photon") {
      particle_ = Particle::Type::photon;
      settings::photon_transport = true;
    } else {
      fatal_error(std::string("Unknown source particle type: ") + temp_str);
    }
  }

  // Check for source strength
  if (check_for_node(node, "strength")) {
    strength_ = std::stod(get_node_value(node, "strength"));
  }

  // Check for external source file
  if (check_for_node(node, "file")) {

  } else {

    // Spatial distribution for external source
    if (check_for_node(node, "space")) {
      // Get pointer to spatial distribution
      pugi::xml_node node_space = node.child("space");

      // Check for type of spatial distribution and read
      std::string type;
      if (check_for_node(node_space, "type"))
        type = get_node_value(node_space, "type", true, true);
      if (type == "cartesian") {
        space_ = UPtrSpace{new CartesianIndependent(node_space)};
      } else if (type == "cylindrical") {
        space_ = UPtrSpace{new CylindricalIndependent(node_space)};
      } else if (type == "spherical") {
        space_ = UPtrSpace{new SphericalIndependent(node_space)};
      } else if (type == "box") {
        space_ = UPtrSpace{new SpatialBox(node_space)};
      } else if (type == "fission") {
        space_ = UPtrSpace{new SpatialBox(node_space, true)};
      } else if (type == "point") {
        space_ = UPtrSpace{new SpatialPoint(node_space)};
      } else {
        fatal_error(fmt::format(
          "Invalid spatial distribution for external source: {}", type));
      }

    } else {
      // If no spatial distribution specified, make it a point source
      space_ = UPtrSpace{new SpatialPoint()};
    }

    // Determine external source angular distribution
    if (check_for_node(node, "angle")) {
      // Get pointer to angular distribution
      pugi::xml_node node_angle = node.child("angle");

      // Check for type of angular distribution
      std::string type;
      if (check_for_node(node_angle, "type"))
        type = get_node_value(node_angle, "type", true, true);
      if (type == "isotropic") {
        angle_ = UPtrAngle{new Isotropic()};
      } else if (type == "monodirectional") {
        angle_ = UPtrAngle{new Monodirectional(node_angle)};
      } else if (type == "mu-phi") {
        angle_ = UPtrAngle{new PolarAzimuthal(node_angle)};
      } else {
        fatal_error(fmt::format(
          "Invalid angular distribution for external source: {}", type));
      }

    } else {
      angle_ = UPtrAngle{new Isotropic()};
    }

    // Determine external source energy distribution
    if (check_for_node(node, "energy")) {
      pugi::xml_node node_dist = node.child("energy");
      energy_ = distribution_from_xml(node_dist);
    } else {
      // Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1
      energy_ = UPtrDist{new Watt(0.988e6, 2.249e-6)};
    }
  }
}

Particle::Bank IndependentSource::sample(uint64_t* seed) const
{
  Particle::Bank site;

  // Set weight to one by default
  site.wgt = 1.0;

  // Repeat sampling source location until a good site has been found
  bool found = false;
  int n_reject = 0;
  static int n_accept = 0;
  while (!found) {
    // Set particle type
    site.particle = particle_;

    // Sample spatial distribution
    site.r = space_->sample(seed);

    // Now search to see if location exists in geometry
    int32_t cell_index, instance;
    double xyz[] {site.r.x, site.r.y, site.r.z};
    int err = openmc_find_cell(xyz, &cell_index, &instance);
    found = (err != OPENMC_E_GEOMETRY);

    // Check if spatial site is in fissionable material
    if (found) {
      auto space_box = dynamic_cast<SpatialBox*>(space_.get());
      if (space_box) {
        if (space_box->only_fissionable()) {
          // Determine material
          const auto& c = model::cells[cell_index];
          auto mat_index = c->material_.size() == 1
            ? c->material_[0] : c->material_[instance];

          if (mat_index == MATERIAL_VOID) {
            found = false;
          } else {
            if (!model::materials[mat_index]->fissionable_) found = false;
          }
        }
      }
    }

    // Check for rejection
    if (!found) {
      ++n_reject;
      if (n_reject >= EXTSRC_REJECT_THRESHOLD &&
          static_cast<double>(n_accept)/n_reject <= EXTSRC_REJECT_FRACTION) {
        fatal_error("More than 95% of external source sites sampled were "
                    "rejected. Please check your external source definition.");
      }
    }
  }

  // Increment number of accepted samples
  ++n_accept;

  // Sample angle
  site.u = angle_->sample(seed);

  // Check for monoenergetic source above maximum particle energy
  auto p = static_cast<int>(particle_);
  auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
  if (energy_ptr) {
    auto energies = xt::adapt(energy_ptr->x());
    if (xt::any(energies > data::energy_max[p])) {
      fatal_error("Source energy above range of energies of at least "
                  "one cross section table");
    } else if (xt::any(energies < data::energy_min[p])) {
      fatal_error("Source energy below range of energies of at least "
                  "one cross section table");
    }
  }

  while (true) {
    // Sample energy spectrum
    site.E = energy_->sample(seed);

    // Resample if energy falls outside minimum or maximum particle energy
    if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
  }

  // Set delayed group
  site.delayed_group = 0;

  return site;
}

//==============================================================================
// FileSource implementation
//==============================================================================

FileSource::FileSource(std::string path)
{
  // Check if source file exists
  if (!file_exists(path)) {
    fatal_error(fmt::format("Source file '{}' does not exist.", path));
  }

  // Read the source from a binary file instead of sampling from some
  // assumed source distribution
  write_message(6, "Reading source file from {}...", path);

  // Open the binary file
  hid_t file_id = file_open(path, 'r', true);

  // Check to make sure this is a source file
  std::string filetype;
  read_attribute(file_id, "filetype", filetype);
  if (filetype != "source" && filetype != "statepoint") {
    fatal_error("Specified starting source file not a source file type.");
  }

  // Read in the source particles
  read_source_bank(file_id, sites_, false);

  // Close file
  file_close(file_id);
}

Particle::Bank FileSource::sample(uint64_t* seed) const
{
  size_t i_site = sites_.size()*prn(seed);
  return sites_[i_site];
}

//==============================================================================
// CustomSourceWrapper implementation
//==============================================================================

CustomSourceWrapper::CustomSourceWrapper(std::string path, std::string parameters)
{
#ifdef HAS_DYNAMIC_LINKING
  // Open the library
  shared_library_ = dlopen(path.c_str(), RTLD_LAZY);
  if (!shared_library_) {
    fatal_error("Couldn't open source library " + path);
  }

  // reset errors
  dlerror();

  // get the function to create the custom source from the library
  auto create_custom_source = reinterpret_cast<create_custom_source_t*>(
    dlsym(shared_library_, "openmc_create_source"));

  // check for any dlsym errors
  auto dlsym_error = dlerror();
  if (dlsym_error) {
    std::string error_msg = fmt::format("Couldn't open the openmc_create_source symbol: {}", dlsym_error);
    dlclose(shared_library_);
    fatal_error(error_msg);
  }

  // create a pointer to an instance of the custom source
  custom_source_ = create_custom_source(parameters);

#else
  fatal_error("Custom source libraries have not yet been implemented for "
    "non-POSIX systems");
#endif
}

CustomSourceWrapper::~CustomSourceWrapper()
{
  // Make sure custom source is cleared before closing shared library
  if (custom_source_.get()) custom_source_.reset();

#ifdef HAS_DYNAMIC_LINKING
  dlclose(shared_library_);
#else
  fatal_error("Custom source libraries have not yet been implemented for "
              "non-POSIX systems");
#endif
}

//==============================================================================
// Non-member functions
//==============================================================================

void initialize_source()
{
  write_message("Initializing source particles...", 5);

  // Generation source sites from specified distribution in user input
  #pragma omp parallel for
  for (int64_t i = 0; i < simulation::work_per_rank; ++i) {
    // initialize random number seed
    int64_t id = simulation::total_gen*settings::n_particles +
      simulation::work_index[mpi::rank] + i + 1;
    uint64_t seed = init_seed(id, STREAM_SOURCE);

    // sample external source distribution
    simulation::source_bank[i] = sample_external_source(&seed);
  }

  // Write out initial source
  if (settings::write_initial_source) {
    write_message("Writing out initial source...", 5);
    std::string filename = settings::path_output + "initial_source.h5";
    hid_t file_id = file_open(filename, 'w', true);
    write_source_bank(file_id, false);
    file_close(file_id);
  }
}

Particle::Bank sample_external_source(uint64_t* seed)
{
  // Determine total source strength
  double total_strength = 0.0;
  for (auto& s : model::external_sources)
    total_strength += s->strength();

  // Sample from among multiple source distributions
  int i = 0;
  if (model::external_sources.size() > 1) {
    double xi = prn(seed)*total_strength;
    double c = 0.0;
    for (; i < model::external_sources.size(); ++i) {
      c += model::external_sources[i]->strength();
      if (xi < c) break;
    }
  }

  // Sample source site from i-th source distribution
  Particle::Bank site {model::external_sources[i]->sample(seed)};

  // If running in MG, convert site.E to group
  if (!settings::run_CE) {
    site.E = lower_bound_index(data::mg.rev_energy_bins_.begin(),
      data::mg.rev_energy_bins_.end(), site.E);
    site.E = data::mg.num_energy_groups_ - site.E - 1.;
  }

  return site;
}

void free_memory_source()
{
  model::external_sources.clear();
}

} // namespace openmc
<|endoftext|>


<commit_before>#include "crypto.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include "sha256.h"

const int PACKET_DATA_LENGTH = 32;    //bytes
const int PACKET_CHECKSUM_LENGTH = 32;
const int PACKET_LENGTH = PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH;

char* OTP(unsigned long long* index, const unsigned long long amount);
char* xorCharArray(const char* first, const char* second, unsigned long long length);
unsigned long long unsignedLongLongRand();
char* longToCharArray(unsigned long long num, int size);
unsigned long long charArrayToLong(const char* data, int size);
bool charArrayEquals(const char* data1, const char* data2, int size);
char* concat(const char* left, const char* right, int sizel, int sizer);
char* sha_256(char* buf);

// send command
ssize_t cwrite(int fd, const void *buf, size_t count) {
	//long indexOfPad = random integer from 0 to size of OTP in bytes
	unsigned long long* indexOfPad = unsignedLongLongRand();
	//char* nonce = longToCharArray(&indexOfPad, PACKET_LENGTH);
	char* nonce = longToCharArray(*indexOfPad, PACKET_LENGTH);
	//send nonce to reciever
	int n;
	n = write(fd, nonce, PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//1
	//char* myChecksum = xor(OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH), OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH));
	char* myChecksum = xorCharArray(OTP(&indexOfPad,PACKET_LENGTH), OTP(&indexOfPad, PACKET_LENGTH));
	//2
	//byte* response = response from receiver
	char buffer[PACKET_LENGTH];
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		n = write(fd, "NO", PACKET_LENGTH);
		if (n < 0) {
			return -1;
		}
		return -1;
	}
	else if (n == 0) {
		//error - no message
		n = write(fd, "NO", PACKET_LENGTH);
		if (n < 0) {
			return -1;
		}
		return -1;
	}
	else if(!charArrayEquals(buffer, myChecksum, PACKET_LENGTH)) {
		//myChecksum != response
		return -1;
	}
	//send ok to reciever
	n = write(fd, "OK", PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//2.1
	//2.2
	//for(int bufferIndex = 0; bufferIndex < len; bufferIndex += PACKET_DATA_LENGTH) {
	for(int bufferIndex = 0; bufferIndex < len; bufferIndex += PACKET_DATA_LENGTH) {
		//char* toSend = buf[bufferIndex];
		char* toSend = buf[bufferIndex];
		//char* key = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* key = OTP(indexOfPad, PACKET_LENGTH);
		//char* mac = sha-256(concat(toSend, key));
		char* mac = sha_256(concat(toSend, key, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH));
		//char* message = concat(toSend, mac);
		char* message = concat(toSend, mac, bufferIndex, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH);
		//char* pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* pad = OTP(indexOfPad, PACKET_LENGTH);
		//send xor(message, pad) to reciever
		n = write(fd, xorCharArray(message, pad), PACKET_LENGTH);
		if (n < 0) {
			delete[] key;
			return -1;
		}
		delete[] key;
		//3
	}
}

// recv command
ssize_t cread(int fd, void *buf, size_t count) {
	//1
	//wait for nonce from sender
	char buffer[PACKET_LENGTH];
	int n;
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		return -1;
	}
	else if (n == 0) {
		//error - no message
		return -1;
	}
	//long indexOfPad;
	//indexOfPad = charArrayToLong(message recieved from sender);
	unsigned long long indexOfPad = charArrayToLong(buffer, PACKET_LENGTH);
	//byte* message = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
	char* message = OTP(&indexOfPad, PACKET_LENGTH);
	//byte* pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
	char* pad = OTP(&indexOfPad, PACKET_LENGTH);
	//byte* reply = xor(message, pad);
	char* reply = xorCharArray(message, pad, PACKET_LENGTH);
	//send reply to sender
	n = write(fd, reply, PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//2
	//2.1
	//recieve ok, if not retry, abort, ignore?
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		return -1;
	}
	else if (n == 0) {
		//error - no message
		return -1;
	}
	else if(std::strcmp(buffer, "OK") != 0) {
		//error - not ok
		return -1;
	}
	//2.2
	//int amount_recv = 0;
	int amount_recv = 0;
	//while(more to recieve or amount_recv + PACKET_DATA_LENGTH < len) {
	while(amount_recv + PACKET_DATA_LENGTH < len) {
		//3
		//byte data[PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH] = message recieved from sender
		char data[PACKET_LENGTH];
		amount_recv += read(fd, data, PACKET_LENGTH);
		//byte* key = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* key = OTP(&indexOfPad, PACKET_LENGTH);
		//pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* pad = OTP(&indexOfPad, PACKET_LENGTH);
		//byte* messsage = xor(data, pad);
		char* message = xorCharArray(data, pad, PACKET_LENGTH);
		//byte* checksum = data + PACKET_DATA_LENGTH;     checksum is last part after the data
		char* checksum = message + PACKET_DATA_LENGTH;
		//byte* mac = sha-256(concat(messsage, key));     recompute mac
		//std::string msgStr(message);
		//std::string keyStr(key);
		char* mac = sha_256(concat(message, key, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH));
		if(!charArrayEquals(checksum, mac, PACKET_LENGTH)) {
			//checksum != mac
			delete[] mac;
			return -1;
		}

		//copy message into buffer
		for(int i = 0; i < PACKET_DATA_LENGTH; i++) {
			buf[i] = message[i];
		}
		buf += PACKET_DATA_LENGTH;
		delete[] mac;
	}
	return amount_recv;
}

char* OTP(unsigned long long* index, const unsigned long long amount) {
	std::ifstream pad;
	pad.open("otp.key");
	pad.seekg(*index);
	char* result = new char[amount];
	pad.read(result, amount);
	pad.close();
	*index += amount;
	return result;
	//byte pad[amount];  allocate amount bytes to return
	//seek to index in OTP
	//for(long i = 0; i < amount; i++)
		//pad[i] = readByte();  read in each byte from the file
	//*index += amount;  increment index so we don't use the same pad twice
	//return pad;
}

char* xorCharArray(const char* first, const char* second, unsigned long long length) {
	//byte result[length];
	char* result = new char[length];
	//for(long i = 0; i < length; i++) {
	for(unsigned long long i = 0; i < length; i++) {
		result[i] = first[i] ^ second[i];
	}
	return result;
}

unsigned long long unsignedLongLongRand() {
    unsigned long long rand1 = abs(rand());
    unsigned long long rand2 = abs(rand());
    rand1 <<= (sizeof(int)*8);
    unsigned long long randULL = (rand1 | rand2);
    return randULL;
}

char* longToCharArray(unsigned long long num, int size) {
	return reinterpret_cast<char*>(num);
}

unsigned long long charArrayToLong(const char* data, int size){
	return reinterpret_cast<unsigned long long>(data);
}

bool charArrayEquals(const char* data1, const char* data2, int size) {
	for(int i = 0; i < size; i++) {
		if(data1[i] != data2[i]) return false;
	}
	return true;
}

char* concat(const char* left, const char* right, int sizel, int sizer) {
	char* result = new char[sizel + sizer];
	for(int i = 0; i < sizel; i++) {
		result[i] = left[i];
	}
	for(int i = 0; i < sizer; i++) {
		result[sizel + i] = right[i];
	}
	return result;
	//std::string msgStr(left);
	//std::string keyStr(right);
	//return &(left + right)[0];
}

char* sha_256(char* buf) {
	return &sha256(buf)[0];
}
<commit_msg>changed var name<commit_after>#include "crypto.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include "sha256.h"

const int PACKET_DATA_LENGTH = 32;    //bytes
const int PACKET_CHECKSUM_LENGTH = 32;
const int PACKET_LENGTH = PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH;

char* OTP(unsigned long long* index, const unsigned long long amount);
char* xorCharArray(const char* first, const char* second, unsigned long long length);
unsigned long long unsignedLongLongRand();
char* longToCharArray(unsigned long long num, int size);
unsigned long long charArrayToLong(const char* data, int size);
bool charArrayEquals(const char* data1, const char* data2, int size);
char* concat(const char* left, const char* right, int sizel, int sizer);
char* sha_256(char* buf);

// send command
ssize_t cwrite(int fd, const void *buf, size_t len) {
	//long indexOfPad = random integer from 0 to size of OTP in bytes
	unsigned long long* indexOfPad = unsignedLongLongRand();
	//char* nonce = longToCharArray(&indexOfPad, PACKET_LENGTH);
	char* nonce = longToCharArray(*indexOfPad, PACKET_LENGTH);
	//send nonce to reciever
	int n;
	n = write(fd, nonce, PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//1
	//char* myChecksum = xor(OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH), OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH));
	char* myChecksum = xorCharArray(OTP(&indexOfPad,PACKET_LENGTH), OTP(&indexOfPad, PACKET_LENGTH));
	//2
	//byte* response = response from receiver
	char buffer[PACKET_LENGTH];
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		n = write(fd, "NO", PACKET_LENGTH);
		if (n < 0) {
			return -1;
		}
		return -1;
	}
	else if (n == 0) {
		//error - no message
		n = write(fd, "NO", PACKET_LENGTH);
		if (n < 0) {
			return -1;
		}
		return -1;
	}
	else if(!charArrayEquals(buffer, myChecksum, PACKET_LENGTH)) {
		//myChecksum != response
		return -1;
	}
	//send ok to reciever
	n = write(fd, "OK", PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//2.1
	//2.2
	//for(int bufferIndex = 0; bufferIndex < len; bufferIndex += PACKET_DATA_LENGTH) {
	for(int bufferIndex = 0; bufferIndex < len; bufferIndex += PACKET_DATA_LENGTH) {
		//char* toSend = buf[bufferIndex];
		char* toSend = buf[bufferIndex];
		//char* key = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* key = OTP(indexOfPad, PACKET_LENGTH);
		//char* mac = sha-256(concat(toSend, key));
		char* mac = sha_256(concat(toSend, key, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH));
		//char* message = concat(toSend, mac);
		char* message = concat(toSend, mac, bufferIndex, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH);
		//char* pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* pad = OTP(indexOfPad, PACKET_LENGTH);
		//send xor(message, pad) to reciever
		n = write(fd, xorCharArray(message, pad), PACKET_LENGTH);
		if (n < 0) {
			delete[] key;
			return -1;
		}
		delete[] key;
		//3
	}
}

// recv command
ssize_t cread(int fd, void *buf, size_t len) {
	//1
	//wait for nonce from sender
	char buffer[PACKET_LENGTH];
	int n;
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		return -1;
	}
	else if (n == 0) {
		//error - no message
		return -1;
	}
	//long indexOfPad;
	//indexOfPad = charArrayToLong(message recieved from sender);
	unsigned long long indexOfPad = charArrayToLong(buffer, PACKET_LENGTH);
	//byte* message = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
	char* message = OTP(&indexOfPad, PACKET_LENGTH);
	//byte* pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
	char* pad = OTP(&indexOfPad, PACKET_LENGTH);
	//byte* reply = xor(message, pad);
	char* reply = xorCharArray(message, pad, PACKET_LENGTH);
	//send reply to sender
	n = write(fd, reply, PACKET_LENGTH);
	if (n < 0) {
		return -1;
	}
	//2
	//2.1
	//recieve ok, if not retry, abort, ignore?
	n = read(fd, buffer, PACKET_LENGTH);
	if (n < 0) {
		//error
		return -1;
	}
	else if (n == 0) {
		//error - no message
		return -1;
	}
	else if(std::strcmp(buffer, "OK") != 0) {
		//error - not ok
		return -1;
	}
	//2.2
	//int amount_recv = 0;
	int amount_recv = 0;
	//while(more to recieve or amount_recv + PACKET_DATA_LENGTH < len) {
	while(amount_recv + PACKET_DATA_LENGTH < len) {
		//3
		//byte data[PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH] = message recieved from sender
		char data[PACKET_LENGTH];
		amount_recv += read(fd, data, PACKET_LENGTH);
		//byte* key = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* key = OTP(&indexOfPad, PACKET_LENGTH);
		//pad = OTP(&indexOfPad, PACKET_DATA_LENGTH + PACKET_CHECKSUM_LENGTH);
		char* pad = OTP(&indexOfPad, PACKET_LENGTH);
		//byte* messsage = xor(data, pad);
		char* message = xorCharArray(data, pad, PACKET_LENGTH);
		//byte* checksum = data + PACKET_DATA_LENGTH;     checksum is last part after the data
		char* checksum = message + PACKET_DATA_LENGTH;
		//byte* mac = sha-256(concat(messsage, key));     recompute mac
		//std::string msgStr(message);
		//std::string keyStr(key);
		char* mac = sha_256(concat(message, key, PACKET_DATA_LENGTH, PACKET_CHECKSUM_LENGTH));
		if(!charArrayEquals(checksum, mac, PACKET_LENGTH)) {
			//checksum != mac
			delete[] mac;
			return -1;
		}

		//copy message into buffer
		for(int i = 0; i < PACKET_DATA_LENGTH; i++) {
			buf[i] = message[i];
		}
		buf += PACKET_DATA_LENGTH;
		delete[] mac;
	}
	return amount_recv;
}

char* OTP(unsigned long long* index, const unsigned long long amount) {
	std::ifstream pad;
	pad.open("otp.key");
	pad.seekg(*index);
	char* result = new char[amount];
	pad.read(result, amount);
	pad.close();
	*index += amount;
	return result;
	//byte pad[amount];  allocate amount bytes to return
	//seek to index in OTP
	//for(long i = 0; i < amount; i++)
		//pad[i] = readByte();  read in each byte from the file
	//*index += amount;  increment index so we don't use the same pad twice
	//return pad;
}

char* xorCharArray(const char* first, const char* second, unsigned long long length) {
	//byte result[length];
	char* result = new char[length];
	//for(long i = 0; i < length; i++) {
	for(unsigned long long i = 0; i < length; i++) {
		result[i] = first[i] ^ second[i];
	}
	return result;
}

unsigned long long unsignedLongLongRand() {
    unsigned long long rand1 = abs(rand());
    unsigned long long rand2 = abs(rand());
    rand1 <<= (sizeof(int)*8);
    unsigned long long randULL = (rand1 | rand2);
    return randULL;
}

char* longToCharArray(unsigned long long num, int size) {
	return reinterpret_cast<char*>(num);
}

unsigned long long charArrayToLong(const char* data, int size){
	return reinterpret_cast<unsigned long long>(data);
}

bool charArrayEquals(const char* data1, const char* data2, int size) {
	for(int i = 0; i < size; i++) {
		if(data1[i] != data2[i]) return false;
	}
	return true;
}

char* concat(const char* left, const char* right, int sizel, int sizer) {
	char* result = new char[sizel + sizer];
	for(int i = 0; i < sizel; i++) {
		result[i] = left[i];
	}
	for(int i = 0; i < sizer; i++) {
		result[sizel + i] = right[i];
	}
	return result;
	//std::string msgStr(left);
	//std::string keyStr(right);
	//return &(left + right)[0];
}

char* sha_256(char* buf) {
	return &sha256(buf)[0];
}
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>

using namespace std;

/*#define FILE_LOGIN_DEFS "/etc/login.defs"*/
#define FILE_LOGIN_DEFS "./login.defs.test"
#define FILE_PASSWD "/etc/passwd"

vector<string> split(string &s, char delim)
{
    vector<string> vtoken;
    string token;
    stringstream stream(s);
    while(getline(stream, token, delim)) {
        vtoken.push_back(token);
    }
    return vtoken;
}

void dbg_print_vector(vector<string> v)
{
    for(int i=0; i<v.size(); i++)
        cout << "[" << v[i] << "]";
    cout << "\n" ;
}

int find_minuid(ifstream &logindefs, char delimeter)
{
    vector<string> toks;
    string line;

    int vec_size;
    int minuid = 0;

    while(getline(logindefs, line) ) {
        toks = split(line, delimeter);
        //dbg_print_vector(toks);
        vec_size = toks.size();
        if(vec_size && toks[0] == "UID_MIN") {
            istringstream (toks[vec_size-1]) >> minuid;
            cout << toks[0] << " = " << minuid << "\n";
        }
    }

}

int get_minuid(ifstream &logindefs)
{
    int minuid = 0;

    minuid = find_minuid(logindefs, ' ');
    /*if(! minuid)*/
    {
        logindefs.clear();
        logindefs.seekg(0, ios::beg);
        minuid = find_minuid(logindefs, '\t');
    }

    return minuid;
}

int main()
{
    ifstream logindefs(FILE_LOGIN_DEFS);
    ifstream pwd(FILE_PASSWD);
    int minuid = 0;

    if(!logindefs.is_open()) {
        cout << "Unable to open file " << FILE_LOGIN_DEFS << "\n";
        return 1;
    }

    minuid = get_minuid(logindefs);

    logindefs.close();
}
<commit_msg>Basic functionality implemented<commit_after>#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>

#include <pwd.h>

using namespace std;

/*#define FILE_LOGIN_DEFS "/etc/login.defs"*/
#define FILE_LOGIN_DEFS "./login.defs.test"
#define FILE_PASSWD "/etc/passwd"

vector<string> split(string &s, char delim)
{
    vector<string> vtoken;
    string token;
    stringstream stream(s);
    while(getline(stream, token, delim)) {
        vtoken.push_back(token);
    }
    return vtoken;
}

void dbg_print_vector(vector<string> v)
{
    for(int i=0; i<v.size(); i++)
        cout << "[" << v[i] << "]";
    cout << "\n" ;
}

int find_minuid(ifstream &logindefs, char delimeter)
{
    vector<string> toks;
    string line;

    int vec_size;
    int minuid = 0;

    while(getline(logindefs, line) ) {
        toks = split(line, delimeter);
        //dbg_print_vector(toks);
        vec_size = toks.size();
        if(vec_size && toks[0] == "UID_MIN") {
            istringstream (toks[vec_size-1]) >> minuid;
            //cout << toks[0] << " = " << minuid << "\n";
        }
    }

    return minuid;
}

int get_minuid(ifstream &logindefs)
{
    int minuid = 0;

    minuid = find_minuid(logindefs, ' ');
    if(! minuid)
    {
        logindefs.clear();
        logindefs.seekg(0, ios::beg);
        minuid = find_minuid(logindefs, '\t');
    }

    return minuid;
}

int main()
{
    ifstream logindefs(FILE_LOGIN_DEFS);
    int minuid = 0;
    struct passwd *pwent = NULL;

    if(!logindefs.is_open()) {
        cout << "Unable to open file " << FILE_LOGIN_DEFS << " !\n";
        return 1;
    }

    minuid = get_minuid(logindefs);
    if(! minuid) {
        cout << "UID_MIN not found!\n";
        return 1;
    }

    cout << "List of users with uid >= " << minuid << ":\n";
    while(pwent = getpwent()) {
        if(pwent->pw_uid >= minuid) {
            cout << pwent->pw_name << " " << pwent->pw_uid <<  " " << pwent->pw_shell << "\n";
        }
    }
    endpwent();

    logindefs.close();
}
<|endoftext|>