$ bitcoin-cli help lockunspent
lockunspent unlock [{"txid":"txid","vout":n},...]

更新不可花费输出的临时列表。
临时加锁(unlock=false)或解锁(unlock=true)指定的交易输出。
一个锁定的交易输出,当花费比特币时,将不会被自动币选择器选中。
锁定只存储在内存中。节点启动时零个锁定的输出,且当一个节点停止或崩溃时,锁定的输出列表总会被清空(通过进程退出)。
也可以查看 listunspent 调用

参数:
1. unlock            (布尔型,必备)指定交易是否解锁(true)或上锁(false)
2. transactions      (字符串,可选)一个 json 对象数组。每个对象的交易索引(字符串)和交易输出序号(数字)
     [               (json 对象的 json 数组)
       {
         "txid":"id",(字符串)交易索引
         "vout": n   (数字)输出序号
       }
       ,...
     ]

结果:
true|false(布尔型)命令是否成功

例子:

列出未花费的交易
> bitcoin-cli listunspent

锁定一笔未花费的交易
> bitcoin-cli lockunspent false "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":1}]"

列出锁定的交易
> bitcoin-cli listlockunspent

再次解锁交易
> bitcoin-cli lockunspent true "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":1}]"

作为一个 json rpc 调用
> curl --user myusername:mypassword --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "lockunspent", "params": [false, "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":1}]"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/

源码剖析

lockunspent 对应的函数在文件 rpcserver.h 中被引用。

extern UniValue lockunspent(const UniValue& params, bool fHelp);

实现在文件 rpcwallet.cpp 中。

UniValue lockunspent(const UniValue& params, bool fHelp)
{
    if (!EnsureWalletIsAvailable(fHelp)) // 1. 确保钱包可用
        return NullUniValue;
    
    if (fHelp || params.size() < 1 || params.size() > 2)
        throw runtime_error(
            "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n"
            "\nUpdates list of temporarily unspendable outputs.\n"
            "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
            "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
            "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
            "is always cleared (by virtue of process exit) when a node stops or fails.\n"
            "Also see the listunspent call\n"
            "\nArguments:\n"
            "1. unlock            (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n"
            "2. \"transactions\"  (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n"
            "     [           (json array of json objects)\n"
            "       {\n"
            "         \"txid\":\"id\",    (string) The transaction id\n"
            "         \"vout\": n         (numeric) The output number\n"
            "       }\n"
            "       ,...\n"
            "     ]\n"

            "\nResult:\n"
            "true|false    (boolean) Whether the command was successful or not\n"

            "\nExamples:\n"
            "\nList the unspent transactions\n"
            + HelpExampleCli("listunspent", "") +
            "\nLock an unspent transaction\n"
            + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
            "\nList the locked transactions\n"
            + HelpExampleCli("listlockunspent", "") +
            "\nUnlock the transaction again\n"
            + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
            "\nAs a json rpc call\n"
            + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
        ); // 2. 帮助内容

    LOCK2(cs_main, pwalletMain->cs_wallet);

    if (params.size() == 1)
        RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); // 3. RPC 类型验证
    else
        RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR));

    bool fUnlock = params[0].get_bool();

    if (params.size() == 1) {
        if (fUnlock)
            pwalletMain->UnlockAllCoins(); // 解锁全部币
        return true;
    }

    UniValue outputs = params[1].get_array(); // 获取交易输出索引数组
    for (unsigned int idx = 0; idx < outputs.size(); idx++) { // 遍历该数组
        const UniValue& output = outputs[idx]; // 获取一个对象(交易输出索引)
        if (!output.isObject())
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
        const UniValue& o = output.get_obj(); // 获取该对象

        RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); // 检查对象类型,"交易索引" 为字符串,"交易输出索引" 为数字型

        string txid = find_value(o, "txid").get_str(); // 获取交易索引
        if (!IsHex(txid)) // 判断是否为 16 进制
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");

        int nOutput = find_value(o, "vout").get_int(); // 获取交易输出索引
        if (nOutput < 0) // 该值大于等于 0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");

        COutPoint outpt(uint256S(txid), nOutput); // 构建一个输出点对象

        if (fUnlock) // 若解锁
            pwalletMain->UnlockCoin(outpt); // 解锁该交易输出
        else // 加锁
            pwalletMain->LockCoin(outpt); // 加锁该交易输出
    }

    return true;
}

1. 确保钱包可用

参考比特币 RPC 命令「fundrawtransaction」1. 确保钱包可用

2. 帮助内容

参考比特币 RPC 命令「getbestblockhash」1. 帮助内容

加解锁函数声明在文件 wallet.h 的钱包类 CWallet 中。

/** 
 * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
 * and provides the ability to create new transactions.
 */
class CWallet : public CCryptoKeyStore, public CValidationInterface
{
    ...
    void LockCoin(COutPoint& output);
    void UnlockCoin(COutPoint& output);
    void UnlockAllCoins();
    ...
};

实现在文件 wallet.cpp 中。

void CWallet::LockCoin(COutPoint& output)
{
    AssertLockHeld(cs_wallet); // setLockedCoins
    setLockedCoins.insert(output); // 加入锁定的交易输出集合
}

void CWallet::UnlockCoin(COutPoint& output)
{
    AssertLockHeld(cs_wallet); // setLockedCoins
    setLockedCoins.erase(output); // 擦除指定的交易输出
}

void CWallet::UnlockAllCoins()
{
    AssertLockHeld(cs_wallet); // setLockedCoins
    setLockedCoins.clear(); // 清空锁定的交易输出集合
}

参考链接