$ bitcoin-cli help importwallet
importwallet "filename"

从一个钱包导出文件(见 dumpwallet)导入。

参数:
1. "filename"(字符串,必备)钱包文件

例子:

导出钱包
> bitcoin-cli dumpwallet "test"

导入钱包
> bitcoin-cli importwallet "test"

使用 json rpc 调用导入
> curl --user myusername:mypassword --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "importwallet", "params": ["test"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/

源码剖析

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

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

实现在文件 rpcwallet.cpp 中。

UniValue importwallet(const UniValue& params, bool fHelp)
{
    if (!EnsureWalletIsAvailable(fHelp)) // 1. 确保钱包可用
        return NullUniValue;
    
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "importwallet \"filename\"\n"
            "\nImports keys from a wallet dump file (see dumpwallet).\n"
            "\nArguments:\n"
            "1. \"filename\"    (string, required) The wallet file\n"
            "\nExamples:\n"
            "\nDump the wallet\n"
            + HelpExampleCli("dumpwallet", "\"test\"") +
            "\nImport the wallet\n"
            + HelpExampleCli("importwallet", "\"test\"") +
            "\nImport using the json rpc call\n"
            + HelpExampleRpc("importwallet", "\"test\"")
        ); // 2. 帮助内容

    if (fPruneMode)
        throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");

    LOCK2(cs_main, pwalletMain->cs_wallet);

    EnsureWalletIsUnlocked(); // 3. 确保钱包被解锁

    ifstream file;
    file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate); // 打开指定文件并立刻定位到文件流结尾
    if (!file.is_open())
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");

    int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); // 获取最佳区块创建时间

    bool fGood = true;

    int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); // 通过文件指针的位置获取文件大小,用于显示加载进度
    file.seekg(0, file.beg); // 文件指针定位到文件流开头

    pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
    while (file.good()) { // 文件流状态正常时
        pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
        std::string line;
        std::getline(file, line); // 读取一行
        if (line.empty() || line[0] == '#') // 若该行为空 或 行首字符为 '#'
            continue; // 跳过空行 或 注释行

        std::vector<std::string> vstr;
        boost::split(vstr, line, boost::is_any_of(" ")); // 按空格 " " 分隔字符串
        if (vstr.size() < 2) // 字符串个数不能低于 2 个
            continue;
        CBitcoinSecret vchSecret;
        if (!vchSecret.SetString(vstr[0])) // Base58 编码的私钥
            continue;
        CKey key = vchSecret.GetKey(); // 获取私钥
        CPubKey pubkey = key.GetPubKey(); // 计算得到公钥
        assert(key.VerifyPubKey(pubkey)); // 验证公钥私钥是否匹配
        CKeyID keyid = pubkey.GetID(); // 获取公钥索引作为密钥索引
        if (pwalletMain->HaveKey(keyid)) { // 检查密钥索引对应密钥是否存在
            LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString());
            continue;
        }
        int64_t nTime = DecodeDumpTime(vstr[1]); // 获取并编码时间
        std::string strLabel; // 保存 label 标签的值,账户名
        bool fLabel = true; // 账户标志,默认为 true
        for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { // 第三个参数,标签类别
            if (boost::algorithm::starts_with(vstr[nStr], "#")) // 没有标签,直接跳出
                break;
            if (vstr[nStr] == "change=1")
                fLabel = false;
            if (vstr[nStr] == "reserve=1")
                fLabel = false;
            if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
                strLabel = DecodeDumpString(vstr[nStr].substr(6)); // 从下标为 6 的字符开始截取字串
                fLabel = true; // 账户标志置为 true
            }
        }
        LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); // 记录导入公钥地址
        if (!pwalletMain->AddKeyPubKey(key, pubkey)) { // 把公私对添加到钱包
            fGood = false;
            continue;
        }
        pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; // 导入私钥创建时间
        if (fLabel) // 若该密钥有所属账户
            pwalletMain->SetAddressBook(keyid, strLabel, "receive"); // 设置到地址簿并设置其所属账户名
        nTimeBegin = std::min(nTimeBegin, nTime);
    }
    file.close(); // 关闭文件输入流
    pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI

    CBlockIndex *pindex = chainActive.Tip(); // 获取链尖区块索引指针
    while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200)
        pindex = pindex->pprev; // 寻找时间相差 2h 的块

    if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey)
        pwalletMain->nTimeFirstKey = nTimeBegin;

    LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
    pwalletMain->ScanForWalletTransactions(pindex); // 从某个块开始扫描块上的交易
    pwalletMain->MarkDirty(); // 标记钱包已改变

    if (!fGood) // 某个密钥添加到钱包失败
        throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");

    return NullUniValue;
}

1. 确保钱包可用

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

2. 帮助内容

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

参考链接