You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CreateCoinStake方法Bug排查:隐身地址私钥正常但奖励转公钥地址

Fixing Stealth Address Stake Reward Bug in CreateCoinStake

Let's break down the issues in your code that are causing stake rewards to go to the public key address instead of your stealth address, even when the private key appears valid:

Key Bugs Identified

1. Incorrect Type Check Logic

The condition checking for supported kernel types is logically flawed:

if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH || whichType != TX_SCRIPTHASH)

This will always evaluate to true (because even if whichType is TX_SCRIPTHASH, the first part whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH will be true, making the whole OR condition true). So your code is incorrectly breaking execution for valid stealth address types.

2. Broken Stealth Address Handling

The TX_SCRIPTHASH (stealth address) branch has multiple critical issues:

  • You're immediately returning true before setting up the output script (scriptPubKeyOut) for the stealth address. This skips all the logic needed to associate the reward with your stealth address.
  • The iterator logic for accessing stealthAddresses is wrong: stealthAddresses.begin()->Encoded() always uses the first stealth address instead of matching the one associated with the kernel script.
  • You're not actually deriving the correct output script for the stealth address, so even if you didn't return early, the reward wouldn't be directed properly.

3. Missing Stealth Address Output Setup

Even if you fixed the return statement, there's no code to create the correct scriptPubKeyOut for the stealth address, which is required to send the reward to that address.

Fixed Code Implementation

Here's the corrected version of the relevant sections, with comments explaining the changes:

bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key) {
    CBlockIndex* pindexPrev = pindexBest;
    CBigNum bnTargetPerCoinDay;
    bnTargetPerCoinDay.SetCompact(nBits);
    txNew.vin.clear();
    txNew.vout.clear();

    // Mark coin stake transaction
    CScript scriptEmpty;
    scriptEmpty.clear();
    txNew.vout.push_back(CTxOut(0, scriptEmpty));

    // Choose coins to use
    int64_t nBalance = GetBalance();
    if (nBalance <= nReserveBalance)
        return false;

    vector<const CWalletTx*> vwtxPrev;
    set<pair<const CWalletTx*,unsigned int> > setCoins;
    int64_t nValueIn = 0;

    // Select coins with suitable depth
    if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity + 10, setCoins, nValueIn))
        return false;
    if (setCoins.empty())
        return false;

    int64_t nCredit = 0;
    CScript scriptPubKeyKernel;
    CTxDB txdb("r");

    BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) {
        CTxIndex txindex;
        {
            LOCK2(cs_main, cs_wallet);
            if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
                continue;
        }

        // Read block header
        CBlock block;
        {
            LOCK2(cs_main, cs_wallet);
            if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
                continue;
        }

        static int nMaxStakeSearchInterval = 60;
        if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
            continue; // only count coins meeting min age requirement

        bool fKernelFound = false;
        for (unsigned int n=0; n<min(nSearchInterval,(int64_t)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown && pindexPrev == pindexBest; n++) {
            // Search backward in time from the given txNew timestamp
            // Search nSearchInterval seconds back up to nMaxStakeSearchInterval
            uint256 hashProofOfStake = 0, targetProofOfStake = 0;
            COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second);

            if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake, targetProofOfStake)) {
                // Found a kernel
                if (fDebug && GetBoolArg("-printcoinstake"))
                    printf("CreateCoinStake : kernel found\n");

                vector<valtype> vSolutions;
                txnouttype whichType;
                CScript scriptPubKeyOut;
                scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey;

                if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) {
                    if (fDebug && GetBoolArg("-printcoinstake"))
                        printf("CreateCoinStake : failed to parse kernel\n");
                    break;
                }

                if (fDebug && GetBoolArg("-printcoinstake"))
                    printf("CreateCoinStake : parsed kernel type=%d\n", whichType);

                // FIX: Correct type check - only proceed if we support the type
                if (!(whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH || whichType == TX_SCRIPTHASH)) {
                    if (fDebug && GetBoolArg("-printcoinstake"))
                        printf("CreateCoinStake : no support for kernel type=%d\n", whichType);
                    break; // only support pay to public key, stealth key and pay to address
                }

                if(whichType == TX_SCRIPTHASH){ // Handle pay-to-stealth address
                    // FIX: Find the matching stealth address for the script hash
                    bool foundStealthKey = false;
                    uint160 scriptHash = Hash160(scriptPubKeyKernel);
                    for (const auto& stealthAddr : stealthAddresses) {
                        if (stealthAddr.GetHash() == scriptHash) {
                            // FIX: Retrieve the private key for this stealth address
                            if (!keystore.GetKey(stealthAddr.GetSpendKey(), key)) {
                                if (fDebug && GetBoolArg("-printcoinstake"))
                                    printf("CreateCoinStake : failed to get key for stealth address\n");
                                break;
                            }
                            // FIX: Create the correct output script for the stealth address
                            scriptPubKeyOut = stealthAddr.GetOutputScript();
                            foundStealthKey = true;
                            break;
                        }
                    }
                    if (!foundStealthKey) {
                        if (fDebug && GetBoolArg("-printcoinstake"))
                            printf("CreateCoinStake : no matching stealth address found\n");
                        break;
                    }
                } else { // Handle non-stealth address types
                    if (whichType == TX_PUBKEYHASH) { // pay to address type
                        // convert to pay to public key type
                        if (!keystore.GetKey(uint160(vSolutions[0]), key)) {
                            if (fDebug && GetBoolArg("-printcoinstake"))
                                printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
                            break; // unable to find corresponding public key
                        }
                        scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
                    }
                    if (whichType == TX_PUBKEY) {
                        valtype& vchPubKey = vSolutions[0];
                        if (!keystore.GetKey(Hash160(vchPubKey), key)) {
                            if (fDebug && GetBoolArg("-printcoinstake"))
                                printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
                            break; // unable to find corresponding public key
                        }
                        if (key.GetPubKey() != vchPubKey) {
                            if (fDebug && GetBoolArg("-printcoinstake"))
                                printf("CreateCoinStake : invalid key for kernel type=%d\n", whichType);
                            break; // keys mismatch
                        }
                        scriptPubKeyOut = scriptPubKeyKernel;
                    }
                }

                txNew.nTime -= n;
                txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
                nCredit += pcoin.first->vout[pcoin.second].nValue;
                vwtxPrev.push_back(pcoin.first);
                txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));

                if (GetWeight(block.GetBlockTime(), (int64_t)txNew.nTime) <= bnTargetPerCoinDay * nCredit / COIN) {
                    fKernelFound = true;
                }
            }
        }
        if (fKernelFound)
            break;
    }

    if (nCredit == 0)
        return false;

    // Calculate reward
    int64_t nReward = GetProofOfStakeReward(txNew.nTime, nCredit);
    txNew.vout[0].nValue = nReward - nFees;

    // Sign the transaction
    if (!SignTransaction(txNew, keystore)) {
        if (fDebug && GetBoolArg("-printcoinstake"))
            printf("CreateCoinStake : failed to sign transaction\n");
        return false;
    }

    return true;
}

Key Fixes Explained

  • Corrected Type Check: Changed the invalid OR/AND condition to properly filter unsupported address types.
  • Proper Stealth Address Matching: Iterates through stealthAddresses to find the one that matches the kernel's script hash, instead of hardcoding the first address.
  • Removed Early Return: Got rid of the premature return true in the stealth address branch, allowing the code to set up the output script and proceed with the transaction.
  • Valid Output Script Creation: Uses stealthAddr.GetOutputScript() to generate the correct script for sending rewards to the stealth address.
  • Correct Private Key Retrieval: Fetches the spend key associated with the matched stealth address instead of using an incorrect lookup.

After applying these fixes, your stake rewards should correctly be sent to the stealth address associated with the staked coins, instead of falling back to the public key address.

内容的提问来源于stack exchange,提问作者Josh Lake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:35:51