text:
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The FFF developers
// Original code was distributed under the MIT software license.
// Copyright (c) 2014-2019 Coin Sciences Ltd
// FFF_Core code distributed under the GPLv3 license, see COPYING file.

#include "core/main.h"
#include "utils/util.h"
#include "FFF_Core/FFF_Core.h"
#include "wallet/wallettxs.h"
#include "community/community.h"

#include <boost/assign/list_of.hpp>

extern mc_WalletTxs* pwalletTxsMain;

using namespace std;
/*
bool AcceptFFF_CoreTransaction(const CTransaction& tx,
                                 const CCoinsViewCache &inputs,
                                 int offset,
                                 bool accept,
                                 string& reason,
                                 int64_t *mandatory_fee_out,
                                 uint32_t *replay);
 */
bool AcceptAdminMinerPermissions(const CTransaction& tx,
                                 int offset,
                                 bool verify_signatures,
                                 string& reason,
                                 uint32_t *result);
bool AcceptAssetTransfers(const CTransaction& tx, const CCoinsViewCache &inputs, string& reason);
bool AcceptAssetGenesis(const CTransaction &tx,int offset,bool accept,string& reason);
bool AcceptPermissionsAndCheckForDust(const CTransaction &tx,bool accept,string& reason);
bool IsTxBanned(uint256 txid);

int CreateUpgradeLists(int current_height,vector<mc_UpgradedParameter> *vParams,vector<mc_UpgradeStatus> *vUpgrades)
{
    mc_EntityDetails entity;
    mc_Buffer *upgrades;
    mc_UpgradeStatus upgrade;
    mc_UpgradedParameter param;

    upgrades=NULL;
    set <uint160> stored_upgrades;
    map <uint64_t,int> map_sorted;
    map <string,int> map_last_upgrade;
    uint160 hash=0;

    int OriginalProtocolVersion=(int)mc_gState->m_NetworkParams->GetInt64Param("protocolversion");
    int NewProtocolVersion=OriginalProtocolVersion;
    int version;
    int err=MC_ERR_NOERROR;

    if(vUpgrades)
    {
        vUpgrades->clear();
    }
    vParams->clear();

    upgrades=mc_gState->m_Permissions->GetUpgradeList(NULL,NULL);

    for(int i=0;i<upgrades->GetCount();i++)
    {
        mc_PermissionDetails *plsRow;
        plsRow=(mc_PermissionDetails *)(upgrades->GetRow(i));
        if(plsRow->m_Type == MC_PTP_UPGRADE)
        {
            if(vParams)
            {
                memcpy(&hash,plsRow->m_Address,sizeof(uint160));
                stored_upgrades.insert(hash);
            }
            map_sorted.insert(std::make_pair(plsRow->m_LastRow,i));
        }
    }

    if(vParams)
    {
        for(int i=0;i<upgrades->GetCount();i++)
        {
            mc_PermissionDetails *plsRow;
            plsRow=(mc_PermissionDetails *)(upgrades->GetRow(i));
            if(plsRow->m_Type != MC_PTP_UPGRADE)
            {
                memcpy(&hash,plsRow->m_Address,sizeof(uint160));
                if(stored_upgrades.count(hash) == 0)
                {
                    plsRow->m_BlockTo = 0;
                    map_sorted.insert(std::make_pair(plsRow->m_LastRow,i));
                }
            }
        }
    }

/*
    for(int i=0;i<permissions->GetCount();i++)
    {
        mc_PermissionDetails *plsRow;
        plsRow=(mc_PermissionDetails *)(permissions->GetRow(i));
        if(plsRow->m_Type == MC_PTP_UPGRADE)
        {
            map_sorted.insert(std::make_pair(plsRow->m_LastRow,i));
        }
    }
*/
    BOOST_FOREACH(PAIRTYPE(const uint64_t, int)& item, map_sorted)
    {
        int i=item.second;
        mc_PermissionDetails *plsRow;
        plsRow=(mc_PermissionDetails *)(upgrades->GetRow(i));
//        if(plsRow->m_Type == MC_PTP_UPGRADE)
        if(err == MC_ERR_NOERROR)
        {
            memset(&upgrade,0,sizeof(mc_UpgradeStatus));
            memcpy(upgrade.m_EntityShortTxID,plsRow->m_Address,MC_AST_SHORT_TXID_SIZE);
            upgrade.m_ApprovedBlock=current_height+2;
            upgrade.m_AppliedBlock=current_height+2;
            upgrade.m_FirstParam=(int)vParams->size();
            if(plsRow->m_BlockFrom < plsRow->m_BlockTo)
            {
                upgrade.m_ApprovedBlock=plsRow->m_BlockReceived;
                if(mc_gState->m_Assets->FindEntityByShortTxID(&entity,plsRow->m_Address))
                {
                    int applied_height=entity.UpgradeStartBlock();
                    if((int)plsRow->m_BlockReceived > applied_height)
                    {
                        applied_height=plsRow->m_BlockReceived;
                    }
                    upgrade.m_AppliedBlock=applied_height;
                    if(current_height >= applied_height)
                    {
                        version=entity.UpgradeProtocolVersion();
                        if(version > 0)
                        {
                            param.m_Param=mc_gState->m_NetworkParams->FindParam("protocolversion");
                            param.m_Value=version;
                            param.m_Block=upgrade.m_AppliedBlock;
                            param.m_Skipped=MC_PSK_APPLIED;
                            if(version >= mc_gState->MinProtocolDowngradeVersion())
                            {
                                if((NewProtocolVersion < mc_gState->MinProtocolForbiddenDowngradeVersion()) || (version >= NewProtocolVersion))
                                {
                                    NewProtocolVersion=version;
                                    if( mc_gState->IsSupported(version) == 0 )
                                    {
                                        err=MC_ERR_NOT_SUPPORTED;
                                    }
                                }
                                else
                                {
                                    param.m_Skipped = MC_PSK_OLD_NOT_DOWNGRADABLE;
                                }
                            }
                            else
                            {
                                param.m_Skipped = MC_PSK_NEW_NOT_DOWNGRADABLE;
                            }
                            vParams->push_back(param);
                        }

                        if(err == MC_ERR_NOERROR)
                        {
                            int size=0;
                            char* ptr=(char*)entity.GetParamUpgrades(&size);
                            char* ptrEnd;
                            int param_size,given_size;
                            int64_t param_value;
                            if(ptr)
                            {
                                ptrEnd=ptr+size;
                                while(ptr<ptrEnd)
                                {
                                    param.m_Param=mc_gState->m_NetworkParams->FindParam(ptr);
                                    ptr+=mc_gState->m_NetworkParams->GetParamFromScript(ptr,&param_value,&given_size);
                                    param.m_Value=param_value;
                                    param.m_Block=upgrade.m_AppliedBlock;
                                    param.m_Skipped=MC_PSK_APPLIED;
                                    if(param.m_Param)
                                    {
                                        param_size=mc_gState->m_NetworkParams->CanBeUpgradedByVersion(param.m_Param->m_Name,NewProtocolVersion,0);
                                        if( (param_size > 0) && (param_size == given_size) )
                                        {
                                            if(mc_gState->m_NetworkParams->IsParamUpgradeValueInRange(param.m_Param,NewProtocolVersion,param_value))
                                            {
                                                bool take_it=true;
                                                string param_name=string(param.m_Param->m_Name);
                                                map <string,int>::iterator it = map_last_upgrade.find(param_name);

                                                if (it != map_last_upgrade.end())
                                                {
                                                    take_it=false;
                                                    if(( param.m_Param->m_Type & MC_PRM_DATA_TYPE_MASK) == MC_PRM_BOOLEAN )
                                                    {
                                                        take_it=true;
                                                    }
                                                    else
                                                    {
                                                        if( ( (param.m_Param->m_Type & MC_PRM_TIME) == 0 ) ||
                                                               ((*vParams)[it->second].m_Block + MIN_BLOCKS_BETWEEN_UPGRADES <= upgrade.m_AppliedBlock) )
                                                        {
                                                            int64_t old_value=(*vParams)[it->second].m_Value;
                                                            if(param.m_Value >= old_value)
                                                            {
                                                                if(param_value <= 2*old_value)
                                                                {
                                                                    take_it=true;
                                                                }
                                                            }
                                                            else
                                                            {
                                                                if(old_value <= 2*param_value)
                                                                {
                                                                    take_it=true;
                                                                }
                                                            }
                                                            if(!take_it)
                                                            {
                                                                param.m_Skipped =MC_PSK_DOUBLE_RANGE;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            param.m_Skipped = MC_PSK_FRESH_UPGRADE;
                                                        }
                                                    }
                                                    if(take_it)
                                                    {
                                                        it->second=(int)vParams->size();
                                                    }
                                                }
                                                else
                                                {
                                                    take_it=false;
                                                    if(( param.m_Param->m_Type & MC_PRM_DATA_TYPE_MASK) == MC_PRM_BOOLEAN )
                                                    {
                                                        take_it=true;
                                                    }
                                                    else
                                                    {
                                                        int64_t old_value=mc_gState->m_NetworkParams->GetInt64Param(param.m_Param->m_Name);

                                                        if(param.m_Value >= old_value)
                                                        {
                                                            if(param_value <= 2*old_value)
                                                            {
                                                                take_it=true;
                                                            }
                                                        }
                                                        else
                                                        {
                                                            if(old_value <= 2*param_value)
                                                            {
                                                                take_it=true;
                                                            }
                                                        }
                                                    }
                                                    if(!take_it)
                                                    {
                                                        param.m_Skipped =MC_PSK_DOUBLE_RANGE;
                                                    }
                                                    else
                                                    {
                                                        map_last_upgrade.insert(std::make_pair(param_name,(int)vParams->size()));
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                param.m_Skipped = MC_PSK_OUT_OF_RANGE;
                                            }
                                        }
                                        else
                                        {
                                            if(param_size > 0)
                                            {
                                                param.m_Skipped = MC_PSK_WRONG_SIZE;
                                            }
                                            else
                                            {
                                                if(param_size < 0)
                                                {
                                                    param.m_Skipped = -param_size;
                                                }
                                                else
                                                {
                                                    param.m_Skipped = MC_PSK_NOT_SUPPORTED;
                                                }
                                            }
                                        }
                                        if(vUpgrades == NULL)                   // Called from FFF_CoreNode_ApplyUpgrades
                                        {
                                            if((int)param.m_Block == current_height)
                                            {
                                                if(param.m_Skipped == MC_PSK_APPLIED)
                                                {
                                                    LogPrintf("PARAMETER UPGRADE: %s = %ld\n",param.m_Param->m_DisplayName,param.m_Value);
                                                }
                                            }
                                        }
                                        vParams->push_back(param);
                                    }
                                    else
                                    {
                                        param.m_Skipped = MC_PSK_NOT_FOUND;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            upgrade.m_LastParam=(int)vParams->size();
            if(vUpgrades)
            {
                vUpgrades->push_back(upgrade);
            }
        }
    }

    mc_gState->m_Permissions->FreePermissionList(upgrades);
    mc_gState->m_ProtocolVersionToUpgrade=NewProtocolVersion;

    return err;
}

bool ReplayMemPool(CTxMemPool& pool, int from,bool accept)
{
    int pos;
    uint256 hash;

    if(mc_gState->m_NetworkParams->IsProtocolFFF_Core() == 0)
    {
        for(pos=from;pos<pool.hashList->m_Count;pos++)
        {
            hash=*(uint256*)pool.hashList->GetRow(pos);
            if(pool.exists(hash))
            {
                if(IsTxBanned(hash))
                {
                    const CTransaction& tx = pool.mapTx[hash].GetTx();
                    string reason;
                    string removed_type="";
                    list<CTransaction> removed;
                    removed_type="banned";
                    LogPrintf("mchn: Tx %s removed from the mempool (%s), reason: %s\n",tx.GetHash().ToString().c_str(),removed_type.c_str(),reason.c_str());
                    pool.remove(tx, removed, true, "replay: banned");
                }
            }
        }
        return true;
    }

    int total_txs=pool.hashList->m_Count;

    LogPrint("mchn", "mchn: Replaying memory pool (%d new transactions, total %d)\n",total_txs-from,total_txs);
    mc_gState->m_Permissions->MempoolPermissionsCopy();

    for(pos=from;pos<pool.hashList->m_Count;pos++)
    {
        hash=*(uint256*)pool.hashList->GetRow(pos);
        if(pool.exists(hash))
        {
            const CTxMemPoolEntry entry=pool.mapTx[hash];
            const CTransaction& tx = entry.GetTx();
            string removed_type="";
            string reason;
            list<CTransaction> removed;

            if(IsTxBanned(hash))
            {
                removed_type="banned";
            }
            else
            {
                int permissions_from,permissions_to;
                permissions_from=mc_gState->m_Permissions->m_MempoolPermissions->GetCount();
                if(entry.FullReplayRequired())
                {
                    LOCK(pool.cs);
                    CCoinsView dummy;
                    CCoinsViewCache view(&dummy);
                    CCoinsViewMemPool viewMemPool(pcoinsTip, pool);
                    view.SetBackend(viewMemPool);
                    if(!AcceptFFF_CoreTransaction(tx,view,-1,accept ? MC_AMT_DEFAULT : MC_AMT_NO_ACCEPT,reason,NULL,NULL))
                    {
                        removed_type="rejected";
                    }
                }
                else
                {
                   if(mc_gState->m_Permissions->MempoolPermissionsCheck(entry.ReplayPermissionFrom(),entry.ReplayPermissionTo()) == 0)
                   {
                        removed_type="rejected";
                   }
                }
                if(removed_type.size() == 0)
                {
                    permissions_to=mc_gState->m_Permissions->m_MempoolPermissions->GetCount();
                    pool.mapTx[hash].SetReplayNodeParams(entry.FullReplayRequired(),permissions_from,permissions_to);
                }
            }

            if(removed_type.size())
            {
                LogPrintf("mchn: Tx %s removed from the mempool (%s), reason: %s\n",tx.GetHash().ToString().c_str(),removed_type.c_str(),reason.c_str());
                pool.remove(tx, removed, true, "replay: "+removed_type);
            }
            else
            {
                if(pwalletTxsMain->AddTx(NULL,tx,-1,NULL,-1,0))
                {
                    removed_type="error";
                    reason="wallet";
                    LogPrintf("mchn: Tx %s removed from the mempool (%s), reason: %s\n",tx.GetHash().ToString().c_str(),removed_type.c_str(),reason.c_str());
                    pool.remove(tx, removed, true, "replay: "+reason);
                }
            }
        }
    }

    return true;
}

void FindSigner(CBlock *block,unsigned char *sig,int *sig_size,uint32_t *hash_type)
{
    int key_size;
    block->vSigner[0]=0;

    if(mc_gState->m_NetworkParams->IsProtocolFFF_Core())
    {
        for (unsigned int i = 0; i < block->vtx.size(); i++)
        {
            const CTransaction &tx = block->vtx[i];
            if (tx.IsCoinBase())
            {
                for (unsigned int j = 0; j < tx.vout.size(); j++)
                {
                    mc_gState->m_TmpScript1->Clear();

                    const CScript& script1 = tx.vout[j].scriptPubKey;
                    CScript::const_iterator pc1 = script1.begin();

                    mc_gState->m_TmpScript1->SetScript((unsigned char*)(&pc1[0]),(size_t)(script1.end()-pc1),MC_SCR_TYPE_SCRIPTPUBKEY);

                    for (int e = 0; e < mc_gState->m_TmpScript1->GetNumElements(); e++)
                    {
                        if(block->vSigner[0] == 0)
                        {
                            mc_gState->m_TmpScript1->SetElement(e);
                            *sig_size=255;
                            key_size=255;
                            if(mc_gState->m_TmpScript1->GetBlockSignature(sig,sig_size,hash_type,block->vSigner+1,&key_size) == 0)
                            {
                                block->vSigner[0]=(unsigned char)key_size;
                            }
                        }
                    }
                }
            }
        }
    }
}

bool VerifyBlockSignatureType(CBlock *block)
{
    unsigned char sig[255];
    int sig_size;
    uint32_t hash_type;

    FindSigner(block, sig, &sig_size, &hash_type);
    if(block->vSigner[0])
    {
        if(Params().DisallowUnsignedBlockNonce())
        {
            if(hash_type == BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE)
            {
                LogPrintf("mchn: Nonce not covered by block signature\n");
                block->nSigHashType=BLOCKSIGHASH_INVALID;
                return false;
            }
        }
        else
        {
            if(hash_type == BLOCKSIGHASH_NO_SIGNATURE)
            {
                LogPrintf("mchn: Nonce covered by block signature\n");
                block->nSigHashType=BLOCKSIGHASH_INVALID;
                return false;
            }
        }
    }
    return true;
}

bool VerifyBlockSignature(CBlock *block,bool force)
{
    unsigned char sig[255];
    int sig_size;//,key_size;
    uint32_t hash_type;
    uint256 hash_to_verify;
    uint256 original_merkle_root;
    uint32_t original_nonce;
    std::vector<unsigned char> vchSigOut;
    std::vector<unsigned char> vchPubKey;
    std::vector<uint256> savedMerkleTree;

    if(!force)
    {
        if(block->nMerkleTreeType != MERKLETREE_UNKNOWN)
        {
            if(block->nSigHashType == BLOCKSIGHASH_INVALID)
            {
                return false;
            }
            return true;
        }
    }

    block->nMerkleTreeType=MERKLETREE_FULL;
    block->nSigHashType=BLOCKSIGHASH_NONE;

    FindSigner(block, sig, &sig_size, &hash_type);
    if(block->vSigner[0])
    {
        switch(hash_type)
        {
            case BLOCKSIGHASH_HEADER:
                block->nMerkleTreeType=MERKLETREE_NO_COINBASE_OP_RETURN;
                block->nSigHashType=BLOCKSIGHASH_HEADER;
                hash_to_verify=block->GetHash();
                break;
            case BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE:
            case BLOCKSIGHASH_NO_SIGNATURE:

                original_merkle_root=block->hashMerkleRoot;
                original_nonce=block->nNonce;

                block->nMerkleTreeType=MERKLETREE_NO_COINBASE_OP_RETURN;
                savedMerkleTree=block->vMerkleTree;
                block->hashMerkleRoot=block->BuildMerkleTree();
                if(hash_type == BLOCKSIGHASH_NO_SIGNATURE_AND_NONCE)
                {
                    block->nNonce=0;
                }
                hash_to_verify=block->GetHash();

                block->hashMerkleRoot=original_merkle_root;
                block->nNonce=original_nonce;

                block->nMerkleTreeType=MERKLETREE_FULL;
                if(savedMerkleTree.size())
                {
                    block->vMerkleTree=savedMerkleTree;
                }
                else
                {
                    block->BuildMerkleTree();
                }
                break;
            default:
                LogPrintf("mchn: Invalid hash type received in block signature\n");
                block->nSigHashType=BLOCKSIGHASH_INVALID;
                return false;
        }

//        printf("V: %s -> %s\n",hash_to_verify.GetHex().c_str(),block->GetHash().GetHex().c_str());

        vchSigOut=std::vector<unsigned char> (sig, sig+sig_size);
        vchPubKey=std::vector<unsigned char> (block->vSigner+1, block->vSigner+1+block->vSigner[0]);

        CPubKey pubKeyOut(vchPubKey);
        if (!pubKeyOut.IsValid())
        {
            LogPrintf("mchn: Invalid pubkey received in block signature\n");
            block->nSigHashType=BLOCKSIGHASH_INVALID;
            return false;
        }
        if(!pubKeyOut.Verify(hash_to_verify,vchSigOut))
        {
            LogPrintf("mchn: Wrong block signature\n");
            block->nSigHashType=BLOCKSIGHASH_INVALID;
            return false;
        }
    }
    else
    {
        if(mc_gState->m_NetworkParams->IsProtocolFFF_Core())
        {
            if(block->hashPrevBlock != uint256(0))
            {
                LogPrintf("mchn: Block signature not found\n");
                block->nSigHashType=BLOCKSIGHASH_INVALID;
                return false;
            }
        }
    }

    return true;
}

/* MCHN END */

bool ReadTxFromDisk(CBlockIndex* pindex,int32_t offset,CTransaction& tx)
{
    CAutoFile file(OpenBlockFile(pindex->GetBlockPos(), true), SER_DISK, CLIENT_VERSION);
    if (file.IsNull())
    {
        LogPrintf("VerifyBlockMiner: Could not load block %s (height %d) from disk\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
        return false;
    }
    try
    {
        fseek(file.Get(), offset, SEEK_CUR);
        file >> tx;
    }
    catch (std::exception &e)
    {
        LogPrintf("VerifyBlockMiner: Could not deserialize tx at offset %d block %s (height %d) from disk\n",offset,pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
        return false;
    }

    return true;
}

bool VerifyBlockMiner(CBlock *block_in,CBlockIndex* pindexNew)
{
    if( (mc_gState->m_NetworkParams->IsProtocolFFF_Core() == 0) ||
        (mc_gState->m_NetworkParams->GetInt64Param("supportminerprecheck") == 0) ||
        (MCP_ANYONE_CAN_MINE) )
    {
        pindexNew->fPassedMinerPrecheck=true;
        return true;
    }

    if(pindexNew->pprev == NULL)
    {
        pindexNew->fPassedMinerPrecheck=true;
        return true;
    }

    bool fReject=false;
    const CBlockIndex *pindexFork = chainActive.FindFork(pindexNew);

    if(pindexFork == pindexNew)
    {
        pindexNew->fPassedMinerPrecheck=true;
        return true;
    }

    CBlockIndex *pindex;
    const CBlock *pblock;
    CBlock branch_block;
    vector <CBlockIndex *> branch;
    vector <uint160> miners;
    int pos,branch_size,record,i;
    uint32_t last_after_fork;
    bool fVerify;
    bool fRolledBack;
    uint32_t admin_miner_count;
    int32_t offsets[MC_PLS_SIZE_OFFSETS_PER_ROW];
    uint256 block_hash;
    vector<unsigned char> vchPubKey;

    fRolledBack=false;
    branch_size=pindexNew->nHeight-pindexFork->nHeight;
    branch.resize(branch_size);
    miners.resize(branch_size);

    pos=branch_size-1;
    pindex=pindexNew;

    CBlock last_block;
    CBlock *pblock_last;
    pblock_last=block_in;
    if(block_in == NULL)
    {
        if ( ((pindexNew->nStatus & BLOCK_HAVE_DATA) == 0 ) || !ReadBlockFromDisk(last_block, pindexNew) )
        {
            LogPrintf("VerifyBlockMiner: Block %s (height %d) miner verification skipped - block not found\n",pindexNew->GetBlockHash().ToString().c_str(),pindexNew->nHeight);
            fReject=true;
        }
        pblock_last=&last_block;
    }

    vchPubKey=vector<unsigned char> (pblock_last->vSigner+1, pblock_last->vSigner+1+pblock_last->vSigner[0]);
    CPubKey pubKeyNew(vchPubKey);
    CKeyID pubKeyHashNew=pubKeyNew.GetID();
    miners[pos]=*(uint160*)(pubKeyHashNew.begin());

    if(fReject)
    {
        fReject=false;
        goto exitlbl;
    }

    LogPrint("mchn","VerifyBlockMiner: Block: %d, Fork: %d, Chain: %d\n",pindexNew->nHeight,pindexFork->nHeight,mc_gState->m_Permissions->m_Block);

    while(pindex != pindexFork)
    {
        if(pindex->nStatus & BLOCK_FAILED_MASK)
        {
            LogPrintf("VerifyBlockMiner: Invalid block %s (height %d)\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
            fReject=true;
            goto exitlbl;
        }

        branch[pos]=pindex;
        pos--;
        pindex=pindex->pprev;
    }

    last_after_fork=0;
    fRolledBack=true;
    mc_gState->m_Permissions->RollBackBeforeMinerVerification(pindexFork->nHeight);
    LogPrint("mchn","VerifyBlockMiner: Rolled back to block %d\n",mc_gState->m_Permissions->m_Block);

    for(pos=0;pos<branch_size;pos++)
    {
        LogPrint("mchn","VerifyBlockMiner: Verifying block %d\n",mc_gState->m_Permissions->m_Block+1);
        pindex=branch[pos];
        block_hash=pindex->GetBlockHash();
        fVerify=false;
        pblock=NULL;
        if(pindex == pindexNew)
        {
            fVerify=true;
            pblock=pblock_last;
        }
        if(!fVerify && (mc_gState->m_Permissions->GetBlockMiner(&block_hash,(unsigned char*)&miners[pos],&admin_miner_count) == MC_ERR_NOERROR) )
        {
            LogPrint("mchn","VerifyBlockMiner: Verified block %s (height %d)\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
            if(miners[pos] == miners[branch_size - 1])
            {
                last_after_fork=pindex->nHeight;
            }
            record=1;
            while(mc_gState->m_Permissions->GetBlockAdminMinerGrants(&block_hash,record,offsets) == MC_ERR_NOERROR)
            {
                for(i=0;i<MC_PLS_SIZE_OFFSETS_PER_ROW;i++)
                {
                    if(offsets[i])
                    {
                        CTransaction tx;
                        string reason;
                        if(!ReadTxFromDisk(pindex,offsets[i],tx))
                        {
                            fReject=true;
                            goto exitlbl;
                        }
                        LogPrint("mchn","VerifyBlockMiner: Grant tx %s in block %s (height %d)\n",tx.GetHash().ToString().c_str(),pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
                        if(!AcceptAdminMinerPermissions(tx,offsets[i],false,reason,NULL))
                        {
                            LogPrintf("VerifyBlockMiner: tx %s: %s\n",tx.GetHash().ToString().c_str(),reason.c_str());
                            fReject=true;
                            goto exitlbl;
                        }
                    }
                }
                record++;
            }
            mc_gState->m_Permissions->IncrementBlock(admin_miner_count);
        }
        else
        {
            if(pblock == NULL)
            {
                LogPrint("mchn","VerifyBlockMiner: Unverified block %s (height %d)\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
                if ( ((pindex->nStatus & BLOCK_HAVE_DATA) == 0 ) || !ReadBlockFromDisk(branch_block, pindex) )
                {
                    LogPrintf("VerifyBlockMiner: Could not load block %s (height %d) from disk\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
                    LogPrintf("VerifyBlockMiner: Block %s (height %d) miner verification skipped\n",pindexNew->GetBlockHash().ToString().c_str(),pindexNew->nHeight);
                    fReject=false;                                              // We cannot neglect subsequent blocks, but it is unverified
                    goto exitlbl;
                }
                pblock = &branch_block;
                vchPubKey=vector<unsigned char> (branch_block.vSigner+1, branch_block.vSigner+1+branch_block.vSigner[0]);
                CPubKey pubKey(vchPubKey);
                CKeyID pubKeyHash=pubKey.GetID();
                miners[pos]=*(uint160*)(pubKeyHash.begin());
                if(miners[pos] == miners[branch_size - 1])
                {
                    last_after_fork=pindex->nHeight;
                }
                if(mc_gState->m_Permissions->CanMineBlockOnFork(&miners[pos],pindex->nHeight,last_after_fork) == 0)
                {
                    LogPrintf("VerifyBlockMiner: Permission denied for miner %s received in block signature\n",CFFFAddress(pubKeyHash).ToString().c_str());
                    fReject=true;
                    goto exitlbl;
                }
            }
            else
            {
                LogPrint("mchn","VerifyBlockMiner: New block %s (height %d)\n",pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
                if(mc_gState->m_Permissions->CanMineBlockOnFork(&miners[pos],pindex->nHeight,last_after_fork) == 0)
                {
                    LogPrintf("VerifyBlockMiner: Permission denied for miner %s received in block signature\n",CFFFAddress(pubKeyHashNew).ToString().c_str());
                    fReject=true;
                    goto exitlbl;
                }
            }
            int off=80+GetSizeOfCompactSize(pblock->vtx.size());
            for (unsigned int i = 0; i < pblock->vtx.size(); i++)
            {
                const CTransaction &tx = pblock->vtx[i];
                string reason;
                uint32_t result;
                int mempool_size=mc_gState->m_Permissions->m_MemPool->GetCount();
                if(!AcceptAdminMinerPermissions(tx,off,true,reason,&result))
                {
                    LogPrintf("VerifyBlockMiner: tx %s: %s\n",tx.GetHash().ToString().c_str(),reason.c_str());
                    fReject=true;
                    goto exitlbl;
                }
                if(mempool_size != mc_gState->m_Permissions->m_MemPool->GetCount())
                {
                    LogPrint("mchn","VerifyBlockMiner: Grant tx %s in block %s (height %d)\n",tx.GetHash().ToString().c_str(),pindex->GetBlockHash().ToString().c_str(),pindex->nHeight);
                }

                off+=tx.GetSerializeSize(SER_NETWORK,tx.nVersion);
            }
            mc_gState->m_Permissions->StoreBlockInfo(&miners[pos],&block_hash);
        }
        pindex->fPassedMinerPrecheck=true;
    }

exitlbl:

    if(fRolledBack)
    {
        LogPrint("mchn","VerifyBlockMiner: Restoring chain, block %d\n",mc_gState->m_Permissions->m_Block);
        mc_gState->m_Permissions->RestoreAfterMinerVerification();
        LogPrint("mchn","VerifyBlockMiner: Restored on block %d\n",mc_gState->m_Permissions->m_Block);
    }

    if(fReject)
    {
        LogPrintf("VerifyBlockMiner: Block %s (height %d) miner verification failed\n",pindexNew->GetBlockHash().ToString().c_str(),pindexNew->nHeight);
    }

    return !fReject;
}

bool CheckBlockPermissions(const CBlock& block,CBlockIndex* prev_block,unsigned char *lpMinerAddress)
{
    bool checked=true;
    const unsigned char *genesis_key;
    int key_size;
    vector<unsigned char> vchSigOut;
    vector<unsigned char> vchPubKey;

    LogPrint("mchn","mchn: Checking block signature and miner permissions...\n");

    key_size=255;

    if(mc_gState->m_NetworkParams->IsProtocolFFF_Core() == 0)
    {
        return true;
    }

    mc_gState->m_Permissions->CopyMemPool();
    mc_gState->m_Permissions->ClearMemPool();

    if(checked)
    {
        if(prev_block)
        {
            if((block.nSigHashType == BLOCKSIGHASH_UNKNOWN) || (block.nSigHashType == BLOCKSIGHASH_INVALID))
            {
                checked=false;
            }
            else
            {
                vchPubKey=vector<unsigned char> (block.vSigner+1, block.vSigner+1+block.vSigner[0]);

                CPubKey pubKeyOut(vchPubKey);
                if (!pubKeyOut.IsValid())
                {
                    LogPrintf("mchn: Invalid pubkey received in block signature\n");
                    checked = false;
                }
                if(checked)
                {
                    CKeyID pubKeyHash=pubKeyOut.GetID();
                    memcpy(lpMinerAddress,pubKeyHash.begin(),20);
                    if(!mc_gState->m_Permissions->CanMine(NULL,pubKeyHash.begin()))
                    {
                //                mc_DumpSize("Connection address",pubKeyHash.begin(),20,20);
                        LogPrintf("mchn: Permission denied for miner %s received in block signature\n",CFFFAddress(pubKeyHash).ToString().c_str());
                        checked = false;
                    }
                }
            }
        }
        else
        {
            genesis_key=(unsigned char*)mc_gState->m_NetworkParams->GetParam("genesispubkey",&key_size);
            vchPubKey=vector<unsigned char> (genesis_key, genesis_key+key_size);

            CPubKey pubKeyOut(vchPubKey);
            if (!pubKeyOut.IsValid())
            {
                LogPrintf("mchn: Invalid pubkey received in block signature\n");
                checked = false;
            }
            if(checked)
            {
                CKeyID pubKeyHash=pubKeyOut.GetID();
                memcpy(lpMinerAddress,pubKeyHash.begin(),20);
            }
        }
    }

    mc_gState->m_Permissions->RestoreMemPool();

    return checked;
}


text:
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Copyright (c) 2017-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <miner.h>

#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <policy/policy.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>

#include <test/util/setup_common.h>

#include <boost/test/unit_test.hpp>

#include <memory>

#include <minerfund.h>
#include <pow/aserti32d.h>
#include <pow/pow.h>
#include <streams.h>
#include <util/strencodings.h>

// Set this to 1 to make the CreateNewBlock_validity test print block headers
// and then terminate instead of running the actual test.
#define GENERATE_BLOCK_HEADERS 0

namespace miner_tests {
struct MinerTestingSetup : public TestingSetup {
    void TestPackageSelection(const Config &config, const CScript &scriptPubKey,
                              const std::vector<CTransactionRef> &txFirst)
        EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
    bool TestSequenceLocks(const CTransaction &tx, int flags)
        EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs) {
        return CheckSequenceLocks(*m_node.mempool, tx, flags);
    }
    BlockAssembler AssemblerForTest(const Config &config);
};
} // namespace miner_tests

BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)

static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB);

BlockAssembler MinerTestingSetup::AssemblerForTest(const Config &config) {
    const CChainParams &params = config.GetChainParams();
    BlockAssembler::Options options;
    options.blockMinFeeRate = blockMinFeeRate;
    options.enableMinerFund = config.EnableMinerFund();
    return BlockAssembler(params, *m_node.mempool, options);
}

constexpr uint64_t blockinfo[] = {
    138489447073, 2000123001,   35192735278,  30743516887,  90270421301,
    4746156542,   269517074003, 50530474709,  201348804349, 131694823915,
    96838508997,  54292234081,  146777944833, 194579551989, 17459672428,
    239463286493, 35972158971,  131229981500, 288173081779, 8271781121,
    14054617812,  1168893791,   67365635174,  196656931529, 35800309163,
    17851574064,  26749261103,  14953015794,  22247598424,  16778982303,
    119328830052, 19182627651,  133744171463, 161386696561, 5583120677,
    37193104054,  74777620295,  240886925522, 202169284734, 37158187173,
    180572907622, 34681770166,  218161288478, 5698624760,   123626801781,
    8494330929,   134180517818, 380634240428, 1112112668,   19262695841,
    727120508,    65255482113,  90905170725,  45882969013,  112934652766,
    15470497544,  89665853312,  4833940537,   70579955079,  131318555366,
    51087976648,  18737029675,  112319798991, 154404109467, 164492077002,
    26800008678,  8462192695,   35955108190,  70609431501,  141973644757,
    171937771431, 27571969114,  175359612626, 9514202569,   144770109340,
    18542728507,  67998505071,  274075528823, 202967048402, 18535319865,
    937791320,    60483413889,  43840334254,  38996608745,  135126199815,
    136500720625, 3691630528,   85448072763,  75537584241,  205777394006,
    22255213872,  170642741506, 130688629769, 219159895699, 97098261172,
    118269396602, 87328266489,  185378645008, 10623915215,  7452183400,
    14890598267,  671249038,    97235700784,  35390245092,  144350615945,
    67256243540,  71934634370,  12275123663,  151449715016, 8967451371,
};

// In Logos, half of the block subsidy goes to the miner.
const Amount MINERREWARD = SUBSIDY / 2;
const Amount LOWFEE = 10'000 * SATOSHI;
const Amount HIGHFEE = 10 * LOTUS;
const Amount HIGHERFEE = 4 * LOTUS;

static CBlockIndex CreateBlockIndex(int nHeight)
    EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
    CBlockIndex index;
    index.nHeight = nHeight;
    index.pprev = ::ChainActive().Tip();
    return index;
}

// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
void MinerTestingSetup::TestPackageSelection(
    const Config &config, const CScript &scriptPubKey,
    const std::vector<CTransactionRef> &txFirst) {
    // Test the ancestor feerate transaction selection.
    TestMemPoolEntryHelper entry;

    CScript scriptSig = CScript() << std::vector<uint8_t>{OP_1};

    // Padding so txs are not undersize
    std::vector<uint8_t> padData(100);

    // Test that a medium fee transaction will be selected after a higher fee
    // rate package with a low fee rate parent.
    CMutableTransaction tx;
    tx.vin.resize(1);
    tx.vin[0].scriptSig = scriptSig;
    // vout 0 = OP_RETURN, vout 1 = miner reward
    tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 1);
    tx.vout.resize(2);
    tx.vout[0].nValue = txFirst[0]->vout[1].nValue - 1000 * SATOSHI;
    tx.vout[1].nValue = Amount::zero();
    tx.vout[1].scriptPubKey = CScript() << OP_RETURN << padData;
    // This tx has a low fee: 1000 satoshis.
    // Save this txid for later use.
    TxId parentTxId = tx.GetId();
    Amount parentTxAmount = tx.vout[0].nValue;
    m_node.mempool->addUnchecked(entry.Fee(1000 * SATOSHI)
                                     .Time(GetTime())
                                     .SpendsCoinbase(true)
                                     .FromTx(tx));

    // This tx has a medium fee: 10000 satoshis.
    tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 1);
    tx.vout[0].nValue = txFirst[1]->vout[1].nValue - 10000 * SATOSHI;
    TxId mediumFeeTxId = tx.GetId();
    m_node.mempool->addUnchecked(entry.Fee(10000 * SATOSHI)
                                     .Time(GetTime())
                                     .SpendsCoinbase(true)
                                     .FromTx(tx));

    // This tx has a high fee, but depends on the first transaction.
    tx.vin[0].prevout = COutPoint(parentTxId, 0);
    // 50k satoshi fee.
    tx.vout[0].nValue = parentTxAmount - 50000 * SATOSHI;
    TxId highFeeTxId = tx.GetId();
    Amount highFeeTxAmount = tx.vout[0].nValue;
    m_node.mempool->addUnchecked(entry.Fee(50000 * SATOSHI)
                                     .Time(GetTime())
                                     .SpendsCoinbase(false)
                                     .FromTx(tx));

    std::unique_ptr<CBlockTemplate> pblocktemplate =
        AssemblerForTest(config).CreateNewBlock(scriptPubKey);
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 4);
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[1]->GetId(), parentTxId);
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[2]->GetId(), highFeeTxId);
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[3]->GetId(), mediumFeeTxId);

    // Test that a package below the block min tx fee doesn't get included
    tx.vin[0].prevout = COutPoint(highFeeTxId, 0);
    // 0 fee.
    tx.vout[0].nValue = highFeeTxAmount;
    TxId freeTxId = tx.GetId();
    Amount freeTxAmount = tx.vout[0].nValue;
    m_node.mempool->addUnchecked(entry.Fee(Amount::zero()).FromTx(tx));
    size_t freeTxSize = GetSerializeSize(tx, PROTOCOL_VERSION);

    // Calculate a fee on child transaction that will put the package just
    // below the block min tx fee (assuming 1 child tx of the same size).
    Amount feeToUse = blockMinFeeRate.GetFee(2 * freeTxSize) - SATOSHI;

    tx.vin[0].prevout = COutPoint(freeTxId, 0);
    tx.vout[0].nValue = freeTxAmount - feeToUse;
    TxId lowFeeTxId = tx.GetId();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
    pblocktemplate = AssemblerForTest(config).CreateNewBlock(scriptPubKey);
    // Verify that the free tx and the low fee tx didn't get selected.
    for (const auto &txn : pblocktemplate->block.vtx) {
        BOOST_CHECK(txn->GetId() != freeTxId);
        BOOST_CHECK(txn->GetId() != lowFeeTxId);
    }

    // Test that packages above the min relay fee do get included, even if one
    // of the transactions is below the min relay fee. Remove the low fee
    // transaction and replace with a higher fee transaction
    m_node.mempool->removeRecursive(CTransaction(tx),
                                    MemPoolRemovalReason::REPLACED);
    // Now we should be just over the min relay fee.
    tx.vout[0].nValue -= 2 * SATOSHI;
    lowFeeTxId = tx.GetId();
    m_node.mempool->addUnchecked(entry.Fee(feeToUse + 2 * SATOSHI).FromTx(tx));
    pblocktemplate = AssemblerForTest(config).CreateNewBlock(scriptPubKey);
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 6);
    // Need pointers to be able to swap entries
    const std::vector<TxId> txIds = {freeTxId, lowFeeTxId, parentTxId,
                                     highFeeTxId, mediumFeeTxId};
    // std::sort(std::begin(txIds), std::end(txIds));
    for (const auto &txn : std::vector(pblocktemplate->block.vtx.begin() + 1,
                                       pblocktemplate->block.vtx.end())) {
        const auto found = std::find(txIds.begin(), txIds.end(), txn->GetId());
        BOOST_CHECK_MESSAGE(found != txIds.end(),
                            "Could not find: " << txn->GetId());
    }

    // Test that transaction selection properly updates ancestor fee
    // calculations as ancestor transactions get included in a block. Add a
    // 0-fee transaction that has 2 outputs.
    tx.vin[0].prevout = COutPoint(txFirst[2]->GetId(), 1);
    tx.vout[0].nValue = txFirst[2]->vout[1].nValue - 100000000 * SATOSHI;
    tx.vout[0].scriptPubKey = CScript() << padData << OP_DROP;
    // 1BCC output.
    tx.vout[1].nValue = 100000000 * SATOSHI;
    tx.vout[1].scriptPubKey = CScript();
    TxId freeTxId2 = tx.GetId();
    Amount freeTxAmount2 = tx.vout[0].nValue;
    m_node.mempool->addUnchecked(
        entry.Fee(Amount::zero()).SpendsCoinbase(true).FromTx(tx));

    // This tx can't be mined by itself.
    tx.vin[0].prevout = COutPoint(freeTxId2, 0);
    tx.vout.resize(1);
    feeToUse = blockMinFeeRate.GetFee(freeTxSize);
    tx.vout[0].nValue = freeTxAmount2 - feeToUse;
    TxId lowFeeTxId2 = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
    pblocktemplate = AssemblerForTest(config).CreateNewBlock(scriptPubKey);

    // Verify that this tx isn't selected.
    for (const auto &txn : pblocktemplate->block.vtx) {
        BOOST_CHECK(txn->GetId() != freeTxId2);
        BOOST_CHECK(txn->GetId() != lowFeeTxId2);
    }

    // This tx will be mineable, and should cause lowFeeTxId2 to be selected as
    // well.
    tx.vin[0].prevout = COutPoint(freeTxId2, 1);
    // 10k satoshi fee.
    tx.vout[0].nValue = (100000000 - 10000) * SATOSHI;
    m_node.mempool->addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx));
    pblocktemplate = AssemblerForTest(config).CreateNewBlock(scriptPubKey);
    const auto foundTx = std::find_if(
        pblocktemplate->block.vtx.begin(), pblocktemplate->block.vtx.end(),
        [lowFeeTxId2](const CTransactionRef &a) -> bool {
            return a->GetId() == lowFeeTxId2;
        });

    BOOST_CHECK_MESSAGE(foundTx != pblocktemplate->block.vtx.end(),
                        "Did not find lowFeeTxId2");
}

void TestCoinbaseMessageEB(uint64_t eb, std::string cbmsg,
                           const CTxMemPool &mempool) {
    GlobalConfig config;
    config.SetMaxBlockSize(eb);

    CScript scriptPubKey =
        CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
                              "a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112"
                              "de5c384df7ba0b8d578a4c702b6bf11d5f")
                  << OP_CHECKSIG;

    std::unique_ptr<CBlockTemplate> pblocktemplate =
        BlockAssembler(config, mempool).CreateNewBlock(scriptPubKey);

    CBlock *pblock = &pblocktemplate->block;

    // IncrementExtraNonce creates a valid coinbase and merkleRoot
    unsigned int extraNonce = 0;
    IncrementExtraNonce(pblock, ::ChainActive().Tip(), config.GetMaxBlockSize(),
                        extraNonce);
    std::vector<uint8_t> vec(cbmsg.begin(), cbmsg.end());
    BOOST_CHECK(pblock->vtx[0]->vin[0].scriptSig ==
                (CScript() << CScriptNum(extraNonce) << vec));
}

// Coinbase scriptSig has to contains the correct EB value
// converted to MB, rounded down to the first decimal
BOOST_AUTO_TEST_CASE(CheckCoinbase_EB) {
    TestCoinbaseMessageEB(1000001, "/EB1.0/", *m_node.mempool);
    TestCoinbaseMessageEB(2000000, "/EB2.0/", *m_node.mempool);
    TestCoinbaseMessageEB(8000000, "/EB8.0/", *m_node.mempool);
    TestCoinbaseMessageEB(8320000, "/EB8.3/", *m_node.mempool);
}

// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) {
    // Note that by default, these tests run with size accounting enabled.
    GlobalConfig config;
    // Don't check this functionality in this test.
    config.SetEnableMinerFund(false);
    const CChainParams &chainparams = config.GetChainParams();
    CScript scriptPubKey =
        CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
                              "a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112"
                              "de5c384df7ba0b8d578a4c702b6bf11d5f")
                  << OP_CHECKSIG;
    std::unique_ptr<CBlockTemplate> pblocktemplate;
    CMutableTransaction tx;
    CScript script;
    TestMemPoolEntryHelper entry;
    entry.nFee = 11 * SATOSHI;
    entry.nHeight = 11;

    fCheckpointsEnabled = false;

    // Simple block creation, nothing special yet:
    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));

    // We can't make transactions until we have inputs.
    // Therefore, load 110 blocks :)
    static_assert(sizeof(blockinfo) / sizeof(*blockinfo) == 110,
                  "Should have 110 blocks to import");
    int baseheight = 0;
    std::vector<CTransactionRef> txFirst;
#if GENERATE_BLOCK_HEADERS
    const int64_t anchorTime = ::ChainActive().Tip()->nTime;
    const int32_t anchorHeight = ::ChainActive().Tip()->nHeight + 1;
    int64_t nTimePrev = ::ChainActive().Tip()->GetMedianTimePast();
#endif
    int nHeight = ::ChainActive().Height() + 1;
    int64_t nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
    for (size_t i = 0; i < sizeof(blockinfo) / sizeof(*blockinfo); ++i) {
        // pointer for convenience.
        CBlock *pblock = &pblocktemplate->block;
        {
            LOCK(cs_main);
            // Fill in header
            pblock->SetBlockTime(nTime);
            pblock->nNonce = blockinfo[i];
            pblock->nHeaderVersion = 1;
            pblock->nHeight = nHeight;
            pblock->hashEpochBlock.SetNull();
            pblock->hashExtendedMetadata = SerializeHash('\0');
            CBlockHeader header = pblock->GetBlockHeader();
            pblock->nBits = GetNextWorkRequired(::ChainActive().Tip(), &header,
                                                chainparams);
#if GENERATE_BLOCK_HEADERS
            if (nHeight > 1) {
                pblock->nBits =
                    CalculateASERT(
                        UintToArith256(chainparams.GetConsensus().powLimit),
                        chainparams.GetConsensus().nPowTargetSpacing,
                        nTimePrev - anchorTime, (nHeight - 1) - anchorHeight,
                        UintToArith256(chainparams.GetConsensus().powLimit),
                        chainparams.GetConsensus().nDAAHalfLife)
                        .GetCompact();
            }
#endif
            CMutableTransaction txCoinbase(*pblock->vtx[0]);
            txCoinbase.nVersion = 1;
            // Add padding to prevent undersize
            txCoinbase.vin[0].scriptSig = CScript();
            txCoinbase.vin[0].scriptSig.resize(100);
            // First output is OP_RETURN "logos" <height>
            txCoinbase.vout[0].scriptPubKey =
                CScript() << OP_RETURN << COINBASE_PREFIX << nHeight;
            // Block subsidy adjustst with difficulty, so we have to update the
            // miner fund outputs
            Amount minerReward =
                GetBlockSubsidy(pblock->nBits, chainparams.GetConsensus());
            std::vector<CTxOut> requiredOutputs = GetMinerFundRequiredOutputs(
                chainparams.GetConsensus(), config.EnableMinerFund(),
                ::ChainActive().Tip(), minerReward);
            for (size_t i = 0; i < requiredOutputs.size(); ++i) {
                txCoinbase.vout[i + 2] = requiredOutputs[i];
                minerReward -= requiredOutputs[i].nValue;
            }
            txCoinbase.vout[1].nValue = minerReward;
            txCoinbase.vout[1].scriptPubKey =
                GetScriptForDestination(ScriptHash(CScript() << OP_1));
            pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
            if (txFirst.size() == 0) {
                baseheight = nHeight - 1;
            }
            if (txFirst.size() < 4) {
                txFirst.push_back(pblock->vtx[0]);
            }
            pblock->SetSize(GetSerializeSize(*pblock));
            pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
        }
        std::shared_ptr<const CBlock> shared_pblock =
            std::make_shared<const CBlock>(*pblock);
#if GENERATE_BLOCK_HEADERS
        CDataStream writer(SER_NETWORK, PROTOCOL_VERSION);
        writer << pblock->GetBlockHeader();
        std::cout << HexStr(std::vector(writer.begin(), writer.end()))
                  << std::endl;
        nTimePrev = nTime;
#else
        BOOST_CHECK(
            Assert(m_node.chainman)
                ->ProcessNewBlock(config, shared_pblock, true, nullptr));
#endif
        ++nHeight;
        ++nTime;
        pblock->hashPrevBlock = pblock->GetHash();
    }
#if GENERATE_BLOCK_HEADERS
    return;
#endif
    LOCK(cs_main);
    LOCK(m_node.mempool->cs);

    // Just to make sure we can still make simple blocks.
    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));

    std::vector<uint8_t> padData(100);

    CScript scriptSig = CScript() << std::vector<uint8_t>{OP_1};
    // block size > limit
    tx.vin.resize(1);
    tx.vin[0].scriptSig = scriptSig;
    tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 1);

    tx.vout.resize(1);
    tx.vout[0].nValue = MINERREWARD;
    // 18 * (520char + DROP) + OP_1 = 9433 bytes
    std::vector<uint8_t> vchData(520);
    for (unsigned int i = 0; i < 18; ++i) {
        tx.vout[0].scriptPubKey << vchData << OP_DROP;
    }

    for (unsigned int i = 0; i < 128; ++i) {
        tx.vout[0].nValue -= LOWFEE;
        const TxId txid = tx.GetId();
        // Only first tx spends coinbase.
        bool spendsCoinbase = i == 0;
        m_node.mempool->addUnchecked(entry.Fee(LOWFEE)
                                         .Time(GetTime())
                                         .SpendsCoinbase(spendsCoinbase)
                                         .FromTx(tx));
        tx.vin[0].prevout = COutPoint(txid, 0);
    }

    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));
    m_node.mempool->clear();

    // Orphan in mempool, template creation fails.
    m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
    BOOST_CHECK_EXCEPTION(AssemblerForTest(config).CreateNewBlock(scriptPubKey),
                          std::runtime_error,
                          HasReason("bad-txns-inputs-missingorspent"));
    m_node.mempool->clear();

    // Child with higher priority than parent.
    tx.vin[0].scriptSig = scriptSig;
    tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 1);
    tx.vout[0].nValue = txFirst[1]->vout[1].nValue - HIGHFEE;
    TxId txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    tx.vin[0].prevout = COutPoint(txid, 0);
    tx.vin.resize(2);
    tx.vin[1].scriptSig = scriptSig;
    tx.vin[1].prevout = COutPoint(txFirst[0]->GetId(), 1);
    // First txn output + fresh coinbase - new txn fee.
    tx.vout[0].nValue =
        tx.vout[0].nValue + txFirst[0]->vout[1].nValue - HIGHERFEE;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));
    m_node.mempool->clear();

    // Coinbase in mempool, template creation fails.
    tx.vin.resize(1);
    tx.vin[0].prevout = COutPoint();
    tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
    tx.vout[0].nValue = Amount::zero();
    txid = tx.GetId();
    // Give it a fee so it'll get mined.
    m_node.mempool->addUnchecked(
        entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
    // Should throw bad-tx-coinbase
    BOOST_CHECK_EXCEPTION(AssemblerForTest(config).CreateNewBlock(scriptPubKey),
                          std::runtime_error, HasReason("bad-tx-coinbase"));
    m_node.mempool->clear();

    // Double spend txn pair in mempool, template creation fails.
    tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 1);
    tx.vin[0].scriptSig = scriptSig;
    tx.vout.resize(2);
    tx.vout[0].nValue = txFirst[0]->vout[1].nValue - HIGHFEE;
    tx.vout[0].scriptPubKey = CScript() << OP_1;
    tx.vout[1].nValue = Amount::zero();
    tx.vout[1].scriptPubKey = CScript() << OP_RETURN << padData;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    tx.vout[0].scriptPubKey = CScript() << OP_2;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    BOOST_CHECK_EXCEPTION(AssemblerForTest(config).CreateNewBlock(scriptPubKey),
                          std::runtime_error,
                          HasReason("bad-txns-inputs-missingorspent"));
    m_node.mempool->clear();

    // Subsidy changing.
    nHeight = ::ChainActive().Height();
    // Create an actual 209999-long block chain (without valid blocks).
    BlockHash epochBlockHash;
    while (::ChainActive().Tip()->nHeight < 209999) {
        CBlockIndex *prev = ::ChainActive().Tip();
        if ((prev->nHeight + 1) % EPOCH_NUM_BLOCKS == 0) {
            epochBlockHash = *prev->phashBlock;
        }
        CBlockIndex *next = new CBlockIndex();
        next->phashBlock = new BlockHash(InsecureRand256());
        ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
        next->pprev = prev;
        next->nHeight = prev->nHeight + 1;
        next->hashEpochBlock = epochBlockHash;
        next->BuildSkip();
        ::ChainActive().SetTip(next);
    }
    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));
    // Extend to a 210000-long block chain.
    while (::ChainActive().Tip()->nHeight < 210000) {
        CBlockIndex *prev = ::ChainActive().Tip();
        if ((prev->nHeight + 1) % EPOCH_NUM_BLOCKS == 0) {
            epochBlockHash = *prev->phashBlock;
        }
        CBlockIndex *next = new CBlockIndex();
        next->phashBlock = new BlockHash(InsecureRand256());
        ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
        next->pprev = prev;
        next->nHeight = prev->nHeight + 1;
        next->hashEpochBlock = epochBlockHash;
        next->BuildSkip();
        ::ChainActive().SetTip(next);
    }

    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));

    // Invalid p2sh txn in mempool, template creation fails
    tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 1);
    tx.vin[0].scriptSig = scriptSig;
    tx.vout[0].nValue = txFirst[0]->vout[1].nValue - LOWFEE;
    script = CScript() << OP_0;
    tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
    txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    tx.vin[0].prevout = COutPoint(txid, 0);
    tx.vin[0].scriptSig = CScript()
                          << std::vector<uint8_t>(script.begin(), script.end());
    tx.vout[0].nValue -= LOWFEE;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(
        entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
    // Should throw blk-bad-inputs
    BOOST_CHECK_EXCEPTION(AssemblerForTest(config).CreateNewBlock(scriptPubKey),
                          std::runtime_error, HasReason("blk-bad-inputs"));
    m_node.mempool->clear();

    // Delete the dummy blocks again.
    while (::ChainActive().Tip()->nHeight > nHeight) {
        CBlockIndex *del = ::ChainActive().Tip();
        ::ChainActive().SetTip(del->pprev);
        ::ChainstateActive().CoinsTip().SetBestBlock(
            del->pprev->GetBlockHash());
        delete del->phashBlock;
        delete del;
    }

    // non-final txs in mempool
    SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
    uint32_t flags = 0;
    // height map
    std::vector<int> prevheights;

    // Relative height locked.
    tx.nVersion = 2;
    tx.vin.resize(1);
    prevheights.resize(1);
    // Only 1 transaction.
    tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 1);
    tx.vin[0].scriptSig = scriptSig;
    // TODO: These tests seem to be broken regarding MTP, enforcing BIP68 from
    // genesis makes relative locktime fail when creating a block.
    // Now, we just make the tx sequence BIP68 compliant before adding it to
    // the mempool and rely only on TestSequenceLocks for now.
    // This test is a mess, we're using tx to both do tests on the tx itself
    // and for adding txs to the mempool.
    tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight - 1;
    prevheights[0] = baseheight + 1;
    tx.vout[0].nValue = txFirst[0]->vout[1].nValue - HIGHFEE;
    tx.vout[0].scriptPubKey = CScript() << OP_1;
    tx.nLockTime = 0;
    m_node.mempool->addUnchecked(
        entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
    // txFirst[0] is the 2nd block
    tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1;
    txid = tx.GetId();

    const Consensus::Params &params = chainparams.GetConsensus();

    {
        // Locktime passes.
        TxValidationState state;
        BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
            params, CTransaction(tx), state, flags));
    }

    // Sequence locks fail.
    BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
    // Sequence locks pass on 2nd block.
    BOOST_CHECK(
        SequenceLocks(CTransaction(tx), flags, prevheights,
                      CreateBlockIndex(::ChainActive().Tip()->nHeight + 2)));

    // Relative time locked.
    tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 1);
    // TODO: These tests seem to be broken, enforcing BIP68 from genesis
    // makes relative locktime fail when creating a block.
    // We only rely on TestSequenceLocks for now.
    tx.vin[0].nSequence = 0;
    tx.vout[0].nValue = txFirst[1]->vout[1].nValue - entry.nFee;
    prevheights[0] = baseheight + 2;
    m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
    // txFirst[1] is the 3rd block.
    tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG |
                          (((::ChainActive().Tip()->GetMedianTimePast() + 1 -
                             ::ChainActive()[1]->GetMedianTimePast()) >>
                            CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) +
                           1);
    txid = tx.GetId();

    {
        // Locktime passes.
        TxValidationState state;
        BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
            params, CTransaction(tx), state, flags));
    }

    // Sequence locks fail.
    BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));

    for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
        // Trick the MedianTimePast.
        ::ChainActive()
            .Tip()
            ->GetAncestor(::ChainActive().Tip()->nHeight - i)
            ->nTime += 512;
    }
    // Sequence locks pass 512 seconds later.
    BOOST_CHECK(
        SequenceLocks(CTransaction(tx), flags, prevheights,
                      CreateBlockIndex(::ChainActive().Tip()->nHeight + 1)));
    for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
        // Undo tricked MTP.
        ::ChainActive()
            .Tip()
            ->GetAncestor(::ChainActive().Tip()->nHeight - i)
            ->nTime -= 512;
    }

    // Absolute height locked.
    tx.vin[0].prevout = COutPoint(txFirst[2]->GetId(), 1);
    tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
    tx.vout[0].nValue = txFirst[2]->vout[1].nValue - entry.nFee;
    prevheights[0] = baseheight + 3;
    tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));

    {
        // Locktime fails.
        TxValidationState state;
        BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock(
            params, CTransaction(tx), state, flags));
        BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal");
    }

    // Sequence locks pass.
    BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));

    {
        // Locktime passes on 2nd block.
        TxValidationState state;
        int64_t nMedianTimePast = ::ChainActive().Tip()->GetMedianTimePast();
        BOOST_CHECK(ContextualCheckTransaction(
            params, CTransaction(tx), state, ::ChainActive().Tip()->nHeight + 2,
            nMedianTimePast, nMedianTimePast));
    }

    // Absolute time locked.
    tx.vin[0].prevout = COutPoint(txFirst[3]->GetId(), 1);
    tx.vout[0].nValue = txFirst[3]->vout[1].nValue - entry.nFee;
    tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
    prevheights.resize(1);
    prevheights[0] = baseheight + 4;
    txid = tx.GetId();
    m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));

    {
        // Locktime fails.
        TxValidationState state;
        BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock(
            params, CTransaction(tx), state, flags));
        BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal");
    }

    // Sequence locks pass.
    BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));

    {
        // Locktime passes 1 second later.
        TxValidationState state;
        int64_t nMedianTimePast =
            ::ChainActive().Tip()->GetMedianTimePast() + 1;
        BOOST_CHECK(ContextualCheckTransaction(
            params, CTransaction(tx), state, ::ChainActive().Tip()->nHeight + 1,
            nMedianTimePast, nMedianTimePast));
    }

    // mempool-dependent transactions (not added)
    tx.vin[0].prevout = COutPoint(txid, 0);
    prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
    tx.nLockTime = 0;
    tx.vin[0].nSequence = 0;

    {
        // Locktime passes.
        TxValidationState state;
        BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
            params, CTransaction(tx), state, flags));
    }

    // Sequence locks pass.
    BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
    tx.vin[0].nSequence = 1;
    // Sequence locks fail.
    BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
    tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
    // Sequence locks pass.
    BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
    tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
    // Sequence locks fail.
    BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));

    pblocktemplate = AssemblerForTest(config).CreateNewBlock(scriptPubKey);
    BOOST_CHECK(pblocktemplate);

    // None of the of the absolute height/time locked tx should have made it
    // into the template because we still check IsFinalTx in CreateNewBlock, but
    // relative locked txs will if inconsistently added to g_mempool. For now
    // these will still generate a valid template until BIP68 soft fork.
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3UL);
    // However if we advance height by 1 and time by 512, all of them should be
    // mined.
    for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
        // Trick the MedianTimePast.
        ::ChainActive()
            .Tip()
            ->GetAncestor(::ChainActive().Tip()->nHeight - i)
            ->nTime += 512;
    }
    ::ChainActive().Tip()->nHeight++;
    SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);

    BOOST_CHECK(pblocktemplate =
                    AssemblerForTest(config).CreateNewBlock(scriptPubKey));
    BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5UL);

    ::ChainActive().Tip()->nHeight--;
    SetMockTime(0);
    m_node.mempool->clear();

    TestPackageSelection(config, scriptPubKey, txFirst);

    fCheckpointsEnabled = true;
}

void CheckBlockMaxSize(const Config &config, const CTxMemPool &mempool,
                       uint64_t size, uint64_t expected) {
    gArgs.ForceSetArg("-blockmaxsize", ToString(size));

    BlockAssembler ba(config, mempool);
    BOOST_CHECK_EQUAL(ba.GetMaxGeneratedBlockSize(), expected);
}

BOOST_AUTO_TEST_CASE(BlockAssembler_construction) {
    GlobalConfig config;

    // We are working on a fake chain and need to protect ourselves.
    LOCK(cs_main);

    // Test around historical 1MB (plus one byte because that's mandatory)
    config.SetMaxBlockSize(ONE_MEGABYTE + 1);
    CheckBlockMaxSize(config, *m_node.mempool, 0, 1000);
    CheckBlockMaxSize(config, *m_node.mempool, 1000, 1000);
    CheckBlockMaxSize(config, *m_node.mempool, 1001, 1001);
    CheckBlockMaxSize(config, *m_node.mempool, 12345, 12345);

    CheckBlockMaxSize(config, *m_node.mempool, ONE_MEGABYTE - 1001,
                      ONE_MEGABYTE - 1001);
    CheckBlockMaxSize(config, *m_node.mempool, ONE_MEGABYTE - 1000,
                      ONE_MEGABYTE - 1000);
    CheckBlockMaxSize(config, *m_node.mempool, ONE_MEGABYTE - 999,
                      ONE_MEGABYTE - 999);
    CheckBlockMaxSize(config, *m_node.mempool, ONE_MEGABYTE,
                      ONE_MEGABYTE - 999);

    // Test around default cap
    config.SetMaxBlockSize(DEFAULT_MAX_BLOCK_SIZE);

    // Now we can use the default max block size.
    CheckBlockMaxSize(config, *m_node.mempool, DEFAULT_MAX_BLOCK_SIZE - 1001,
                      DEFAULT_MAX_BLOCK_SIZE - 1001);
    CheckBlockMaxSize(config, *m_node.mempool, DEFAULT_MAX_BLOCK_SIZE - 1000,
                      DEFAULT_MAX_BLOCK_SIZE - 1000);
    CheckBlockMaxSize(config, *m_node.mempool, DEFAULT_MAX_BLOCK_SIZE - 999,
                      DEFAULT_MAX_BLOCK_SIZE - 1000);
    CheckBlockMaxSize(config, *m_node.mempool, DEFAULT_MAX_BLOCK_SIZE,
                      DEFAULT_MAX_BLOCK_SIZE - 1000);

    // If the parameter is not specified, we use
    // DEFAULT_MAX_GENERATED_BLOCK_SIZE
    {
        gArgs.ClearForcedArg("-blockmaxsize");
        BlockAssembler ba(config, *m_node.mempool);
        BOOST_CHECK_EQUAL(ba.GetMaxGeneratedBlockSize(),
                          DEFAULT_MAX_GENERATED_BLOCK_SIZE);
    }
}

BOOST_AUTO_TEST_CASE(TestCBlockTemplateEntry) {
    const CTransaction tx;
    CTransactionRef txRef = MakeTransactionRef(tx);
    CBlockTemplateEntry txEntry(txRef, 1 * SATOSHI, 10);
    BOOST_CHECK_MESSAGE(txEntry.tx == txRef, "Transactions did not match");
    BOOST_CHECK_EQUAL(txEntry.fees, 1 * SATOSHI);
    BOOST_CHECK_EQUAL(txEntry.sigOpCount, 10);
}

BOOST_AUTO_TEST_SUITE_END()

ext: cpp

max_stars_repo_name: ngocviet/lotusd

max_issues_repo_name: ngocviet/lotusd

max_forks_repo_name: ngocviet/lotusd


text:
/*
 05_flatMapReverse.cpp
 Uses boost::flat_map using view::reverse

 */

#include <iostream>
#include <ranges>
#include <string>
#include <boost/container/flat_map.hpp>

#include "rangeNamespace.hpp"

namespace view = std::view;

int main()
{
    boost::container::flat_map<std::string, int> fm {};
    fm["world"] = 2;
    fm["hello"] = 1;

    // not sure about reverse_view
    //for ( auto [k, v]  : rng::reverse_view{fm} ) {
    for (auto [k, v]: fm | view::reverse) {
        std::cout << k << ":" << v << "\n" ;
    }
    //world:2
    //hello:1
}


text:
/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2009: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#ifndef BOOST_ICL_TYPE_TRAITS_HAS_SET_SEMANTICS_HPP_JOFA_100829
#define BOOST_ICL_TYPE_TRAITS_HAS_SET_SEMANTICS_HPP_JOFA_100829

#include <boost/mpl/or.hpp>
#include <boost/mpl/and.hpp>
#include <boost/icl/type_traits/is_set.hpp>
#include <boost/icl/type_traits/is_map.hpp>
#include <boost/icl/type_traits/codomain_type_of.hpp>

namespace boost{ namespace icl
{
    template <class Type> struct has_set_semantics
    {
        typedef has_set_semantics<Type> type;
        BOOST_STATIC_CONSTANT(bool,
            value = (mpl::or_< is_set<Type>
                             , mpl::and_< is_map<Type>
                                        , has_set_semantics
                                          <typename codomain_type_of<Type>::type >
                                        >
                             >::value));
    };

}} // namespace boost icl

#endif

ext: hpp

max_stars_repo_name: rudylee/expo

max_issues_repo_name: rudylee/expo

max_forks_repo_name: rudylee/expo


text:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cmath>
#include <fstream>
#include <valarray>
#include <sstream>
#include <filesystem>
#include <string>
#include <utility>

using namespace std::string_literals;

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/math/constants/constants.hpp>

#include "miMaS/field.h"
#include "miMaS/complex_field.h"
#include "miMaS/weno.h"
#include "miMaS/fft.h"
#include "miMaS/array_view.h"
#include "miMaS/poisson.h"
#include "miMaS/rk.h"
#include "miMaS/config.h"
#include "miMaS/signal_handler.h"
#include "miMaS/iteration.h"

namespace o2 {
  template < typename _T , std::size_t NumDimsV >
  auto
  trp_v ( field<_T,NumDimsV> const & u , ublas::vector<_T> const& E )
  {
    field<_T,NumDimsV> trp(tools::array_view<const std::size_t>(u.shape(),NumDimsV+1));

    { auto k=0, km1=trp.size(0)-1;
      for ( auto i=0 ; i<trp.size(1) ; ++i ) {
        trp[k][i] = ( E(i)*(u[k+1][i]-u[km1][i])/(2.*u.step.dv) );
      }
    }
    for ( auto k=1 ; k<trp.size(0)-1 ; ++k ) {
      for ( auto i=0 ; i<trp.size(1) ; ++i ) {
        trp[k][i] = ( E(i)*(u[k+1][i]-u[k-1][i])/(2.*u.step.dv) );
      }
    }
    { auto k=trp.size(0)-1, kp1=0;
      for ( auto i=0 ; i<trp.size(1) ; ++i ) {
        trp[k][i] = ( E(i)*(u[kp1][i]-u[k-1][i])/(2.*u.step.dv) );
      }
    }

    return trp;
  }
}

namespace math = boost::math::constants;
const std::complex<double> & I = std::complex<double>(0.,1.);

#define SQ(X) ((X)*(X))
#define Xi(i) (i*f.step.dx+f.range.x_min)
#define Vk(k) (k*f.step.dv+f.range.v_min)

struct surimi {
  ublas::vector<double> E;

  surimi ( std::size_t nx , double )
    : E(nx,1.)
  { ; }

  ~surimi ()
  { ; }

  ublas::vector<double>
  operator () ( ublas::vector<double> const& )
  {
    return E;
  }
};

auto
disque ( double x0 , double v0 , double R ) {
  return [=](double x,double v){ return ( SQ( x-x0 ) + SQ(v-v0) < R*R ) ? 1. : 0.; };
}

auto
test2 ( double vmin , double vmax , double kx ) {
  return [=](double x,double v){ return ( vmin < v && v < vmax ) ? std::cos(2.*math::pi<double>()*kx*x) : 0.; };
}

auto
maxwellian ( double rho , double u , double T ) {
  return [=](double x,double v){ return rho/(std::sqrt(2.*math::pi<double>()*T))*std::exp( -0.5*SQ(v-u)/T ); };
}

int
main ( int argc , char const * argv[] )
{
  std::filesystem::path p("config.init");
  if ( argc > 1 )
    { p = argv[1]; }
  auto c = config(p);
  c.name = "cm_e10m3";

  c.create_output_directory();
  std::ofstream ofconfig( c.output_dir / "config.init" );
  ofconfig << c << "\n";
  ofconfig.close();

/* ------------------------------------------------------------------------- */
  field<double,1> f(boost::extents[c.Nv][c.Nx]);
  field<double,1> f_sol(boost::extents[c.Nv][c.Nx]);

  complex_field<double,1> hf(boost::extents[c.Nv][c.Nx]);
  fft::spectrum_ d(c.Nx);

  std::vector<double> mass;

  const double Kx = 0.5;
  f.range.v_min = -3.; f.range.v_max = 3.;
  f.range.x_min = -3.; f.range.x_max = 3.;
  f.compute_steps();

  f_sol.range = f.range;
  f_sol.compute_steps();

  ublas::vector<double> v (c.Nv,1.); // velocity in x direction, transport at speed 1

  ublas::vector<double> kx(c.Nx);
  {
    double l = f.range.len_x();
    for ( auto i=0 ; i<c.Nx/2 ; ++i ) { kx[i]      = 2.*math::pi<double>()*i/l; }
    for ( int i=-c.Nx/2 ; i<0 ; ++i ) { kx[c.Nx+i] = 2.*math::pi<double>()*i/l; }
  }

  auto c0 = disque(0.,0.,1.);
  auto d0 = test2(-1.,1.,1./f.range.len_x());

  double m=0;
  for (field<double,2>::size_type k=0 ; k<f.size(0) ; ++k ) {
    for (field<double,2>::size_type i=0 ; i<f.size(1) ; ++i ) {
      f[k][i] = c0(Xi(i),Vk(k));
      f_sol[k][i] = c0(Xi(i)-c.Tf,Vk(k)-c.Tf);

      m += f[k][i]*f.step.dx*f.step.dv;
      //f[k][i] = d0(Xi(i),Vk(k));
      //f_sol[k][i] = d0(Xi(i)-c.Tf,Vk(k)-c.Tf);
    }
  }
  std::cout << m << std::endl;
  //d.fft(f[int(c.Nv/2.)].begin());
  //std::copy( d.begin() , d.end() , std::ostream_iterator<std::complex<double>>(std::cout," "));

  f.write( c.output_dir / ("init_"+c.name+".dat") );
  f_sol.write( c.output_dir / ("sol_"+c.name+".dat") );

  iteration::iteration<double> iter;
  iter.iter = 0;
  iter.current_time = 0.;
  //iter.dt = 0.050*f.step.dv;
  //iter.dt = 2.*std::sqrt(2.)*f.step.dv;
  iter.dt = 0.150*f.step.dv;

  std::vector<double> normL2; normL2.reserve(100);
  auto L2 = [&]( field<double,1>const& f )->double {
    return std::pow(
            std::accumulate( f.origin() , f.origin()+f.num_elements() , 0. ,
              []( double s , double fik )->double { return s + fik*fik; }
          ),2)*f.step.dx*f.step.dv;
  };

  // space scheme
  auto wenol = [&](field<double,1>const& f , ublas::vector<double> const& E )->field<double,1> { return wenolin::trp_v(f,E); };
  auto weno  = [&](field<double,1>const& f , ublas::vector<double> const& E )->field<double,1> { return weno::trp_v(f,E); };
  auto cd2   = [&](field<double,1>const& f , ublas::vector<double> const& E )->field<double,1> { return o2::trp_v(f,E); };

  // time scheme init
  //expRK::HochbruckOstermann<surimi> rk(c.Nx,c.Nv,f.range.len_x(),f.shape(),v,kx,cd2); // 0.250 , 0.501 , 1.702
  expRK::CoxMatthews<surimi> rk(c.Nx,c.Nv,f.range.len_x(),f.shape(),v,kx,cd2); // 0.150 , 0.450 , 1.351
  //expRK::RK22<surimi> rk(c.Nx,c.Nv,f.range.len_x(),f.shape(),v,kx,cd2);
  //lawson::RK33<surimi> rk(c.Nx,c.Nv,f.range.len_x(),f.shape(),v,kx,weno);

  //while (  iter.current_time < c.Tf ) {
  while ( iter.iter < 101 ) {
    std::cout << "\r" << iteration::time(iter) << std::flush;

    normL2.push_back(L2(f));
    f = rk(f,iter.dt);

    ++iter.iter;
    //if ( iter.current_time + iter.dt > c.Tf ) { iter.dt = c.Tf-iter.current_time; }
    iter.current_time += iter.dt;
  }
  std::cout << "\r" << time(iter) << std::endl;

  f.write( c.output_dir / ("vp_"+c.name+".dat") );

  /*
  field<double,1> f_diff(boost::extents[c.Nv][c.Nx]);
  f_diff.range = f.range;
  for (field<double,2>::size_type k=0 ; k<f.size(0) ; ++k ) {
    for (field<double,2>::size_type i=0 ; i<f.size(1) ; ++i ) {
      f_diff[k][i] = f[k][i]-d0(Xi(i)-c.Tf,Vk(k)-c.Tf);
    }
  }
  f_diff.write( c.output_dir / ("diff_"+c.name+".dat") );
  */

  std::ofstream of( c.output_dir / ("normL2"+c.name+".dat") );
  std::transform(
    normL2.begin() , normL2.end() ,
    std::ostream_iterator<std::string>(of,"\n") ,
    [&,count=0](double x) mutable {
      std::stringstream ss; ss << count << " " << iter.dt*(count++) << " " << x;
      return ss.str();
    }
  );
  of.close();

  return 0;
}

ext: cc

max_stars_repo_name: Kivvix/miMaS

max_issues_repo_name: Kivvix/miMaS

max_forks_repo_name: Kivvix/miMaS


text:
//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_CAST_INTEGER_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_CAST_INTEGER_HPP_INCLUDED

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/detail/dispatch/as.hpp>
#include <boost/simd/detail/traits.hpp>
#include <boost/simd/detail/nsm.hpp>
#include <boost/simd/function/if_allbits_else_zero.hpp>
#include <boost/simd/function/interleave.hpp>
#include <boost/simd/function/combine.hpp>
#include <boost/simd/function/is_ltz.hpp>
#include <boost/simd/function/slice.hpp>
#include <boost/simd/constant/zero.hpp>
#include <type_traits>

namespace boost { namespace simd { namespace detail
{
  // -----------------------------------------------------------------------------------------------
  // upward cast with sign
  template<typename A0, typename A1, bool Sign>
  struct upward_cast
  {
    using result = typename A0::template rebind<typename A1::type>;
    static BOOST_FORCEINLINE result do_(const A0& a0 ) BOOST_NOEXCEPT
    {
      using upw = pack<typename A1::type, A0::static_size/2>;

      #if BOOST_ENDIAN_BIG_BYTE
      auto x = interleave(if_allbits_else_zero(is_ltz(a0)),a0);
      #else
      auto x = interleave(a0,if_allbits_else_zero(is_ltz(a0)));
      #endif

      return combine(bitwise_cast<upw>(x[0]),bitwise_cast<upw>(x[1]));
    }
  };

  template<typename A0, typename A1>
  struct upward_cast<A0, A1, false>
  {
    using result = typename A0::template rebind<typename A1::type>;
    static BOOST_FORCEINLINE result do_(const A0& a0) BOOST_NOEXCEPT
    {
      using upw = bd::upgrade_t<A0, bd::sign_of_t<typename A1::type>>;

      #if BOOST_ENDIAN_BIG_BYTE
      auto x = interleave(Zero<A0>(),a0);
      #else
      auto x = interleave(a0,Zero<A0>());
      #endif

      return combine(bitwise_cast<upw>(x[0]),bitwise_cast<upw>(x[1]));
    }
  };
} } }

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;

  // -----------------------------------------------------------------------------------------------
  // uint8/16 -> ints32/64
  BOOST_DISPATCH_OVERLOAD_IF( pack_cast_
                            , (typename A0, typename A1, typename S, typename X, std::size_t N)
                            , (detail::is_native<X>)
                            , bd::cpu_
                            , bs::pack_<bd::integral_<A0,N,S>, X>
                            , bd::target_< bd::scalar_< bd::ints_<A1,4*N> > >
                            )
  {
    using result = typename A0::template rebind<typename A1::type>;

    BOOST_FORCEINLINE result operator()(const A0 & a0, A1 const&) const BOOST_NOEXCEPT
    {
      using tgt = typename bd::upgrade_t<A0>::value_type;
      auto x = pack_cast<tgt>(a0);

      return combine( pack_cast<typename A1::type>(slice_low(x))
                    , pack_cast<typename A1::type>(slice_high(x))
                    );
    }
  };

  // -----------------------------------------------------------------------------------------------
  // int8 -> ints16
  BOOST_DISPATCH_OVERLOAD_IF( pack_cast_
                            , (typename A0, typename A1, typename X)
                            , (detail::is_native<X>)
                            , bd::cpu_
                            , bs::pack_<bd::ints8_<A0>, X>
                            , bd::target_< bd::scalar_< bd::ints16_<A1> > >
                            )
  {
    using result = typename A0::template rebind<typename A1::type>;
    BOOST_FORCEINLINE result operator() ( const A0 & a0, A1 const& ) const BOOST_NOEXCEPT
    {
      return detail::upward_cast<A0, A1, std::is_signed<typename A0::value_type>::value>::do_(a0);
    }
  };

  // -----------------------------------------------------------------------------------------------
  // int16 -> ints32
  BOOST_DISPATCH_OVERLOAD_IF( pack_cast_
                            , (typename A0, typename A1, typename X)
                            , (detail::is_native<X>)
                            , bd::cpu_
                            , bs::pack_<bd::ints16_<A0>, X>
                            , bd::target_< bd::scalar_< bd::ints32_<A1> > >
                            )
  {
    using result = typename A0::template rebind<typename A1::type>;
    BOOST_FORCEINLINE result operator() ( const A0 & a0, A1 const& ) const BOOST_NOEXCEPT
    {
      return detail::upward_cast<A0, A1, std::is_signed<typename A0::value_type>::value>::do_(a0);
    }
  };

  // -----------------------------------------------------------------------------------------------
  // int32 -> ints64
  BOOST_DISPATCH_OVERLOAD_IF( pack_cast_
                            , (typename A0, typename A1, typename X)
                            , (detail::is_native<X>)
                            , bd::cpu_
                            , bs::pack_<bd::ints32_<A0>, X>
                            , bd::target_< bd::scalar_< bd::ints64_<A1> > >
                            )
  {
    using result = typename A0::template rebind<typename A1::type>;
    BOOST_FORCEINLINE result operator() ( const A0 & a0, A1 const& ) const BOOST_NOEXCEPT
    {
      return detail::upward_cast<A0, A1, std::is_signed<typename A0::value_type>::value>::do_(a0);
    }
  };
} } }

#endif

max_stars_repo_name: SylvainCorlay/pythran

max_issues_repo_name: SylvainCorlay/pythran

max_forks_repo_name: SylvainCorlay/pythran