区块链比特币学习 - 3 - 钱包

Posted 宣之于口

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区块链比特币学习 - 3 - 钱包相关的知识,希望对你有一定的参考价值。

比特币学习 - 3 - 钱包

参考博客:here

一、基本概念

钱包是私钥的容器。比特币钱包只包含私钥而不是比特币。每一个用户有一个包含多个私钥的钱包。钱包中包含成对的私钥和公钥。用户用这些私钥来签名交易,从而证明它们拥有交易的输出(也就是其中的比特币)。

非确定性钱包:一堆密钥,钱包只是随机生成的密钥集合

确定性钱包:从公共的种子(密码学上安全的伪随机数发生器)生成密钥

分层确定性钱包:从一个种子形成树结构密钥体系

二、钱包技术细节

根据图示,我们一步一步看一下细节

1. 助记词

助记词是由钱包使用BIP-39中定义的标准化过程自动生成的

生成流程:

其中单词表可以参考:单词表

2. 种子

从上一步,我们可以得到128~256bits长度的熵以及其对应的助记词。种子的长度为512bits,这里需要运用 密钥延伸函数PBKDF2

PBKDF2的基本原理是通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。

首次盐值为:助记词+用户输入的密码(可选)

生成过程:助记词与盐值进行HMAC-SHA512,然后将生成的值作为盐值,与助记词进行HMAC-SHA512,如此重复,共2048次,最后得到种子。

3. 主密钥

在上一步我们得到了512bits的种子,左边256bits即主私钥,右边256bits即主链编码。

其中主公钥可以通过主私钥获得:264bits

4. 子密钥

分层确定性钱包使用CKD(child key derivation)方程去从母密钥衍生出子密钥。

生成过程:父公钥+父链编码+索引号(32bits,例如0)通过HMAC-SHA512单向哈希函数生成512bits,左边256bits即子私钥,右边256bits即子链编码。

  • 通过改变索引号生成不同子密钥。比如子0,子1,子2等等。每一个母密钥可以右20亿个子密钥。

  • 向密码树下一层重复这个过程,每个子密钥可以依次成为母密钥继续创造它自己的子密钥,直到无限代。

三、创建钱包

参考文章:here

代码目录:src/wallet/wallet.cpp

std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags)

    ...
	// 如果用户设置了”-zapwallettxes”选项,则清除钱包的所有的交易数据。[可能存在交易费过低或者没有交易费的交易,通过这个选项可以清除掉这种交易]
    if (gArgs.GetBoolArg("-zapwallettxes", false))  ... 
	...
    // 创建了CWallet对象,然后调用LoadWallet()方法从wallet.dat文件中加载数据。
    std::shared_ptr<CWallet> walletInstance(new CWallet(name, WalletDatabase::Create(path)), ReleaseWallet);
    DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
	...
	// 升级钱包
    int prev_version = walletInstance->nWalletVersion;
    if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))  ... 
	...
    // 生成主私钥和子私钥
    if (fFirstRun)
    
        ...
        walletInstance->SetMinVersion(FEATURE_LATEST);

        if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) 
            //selective allow to set flags
            walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
         else 
            // 设置新种子,其中调用MakeNewKey
            CPubKey seed = walletInstance->GenerateNewSeed();
            walletInstance->SetHDSeed(seed);
        

        // 填充密钥池,生成子私钥
        if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !walletInstance->TopUpKeyPool()) 
            InitError(_("Unable to generate initial keys"));
            return nullptr;
        

        walletInstance->ChainStateFlushed(chainActive.GetLocator());
    
    ...

其中填充密钥池:

bool CWallet::TopUpKeyPool(unsigned int kpSize)

    ... 
    bool internal = false;
    WalletBatch batch(*database);
    for (int64_t i = missingInternal + missingExternal; i--;)
    
        if (i < missingInternal) 
            internal = true;
        
		... 
        int64_t index = ++m_max_keypool_index;
		// 调用GenerateNewKey()方法生成公钥私钥对
        CPubKey pubkey(GenerateNewKey(batch, internal));
        if (!batch.WritePool(index, CKeyPool(pubkey, internal))) 
            throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
        

        if (internal) 
            setInternalKeyPool.insert(index);
         else 
            setExternalKeyPool.insert(index);
        
        m_pool_key_to_index[pubkey.GetID()] = index;
    
    ...

四、钱包交易数据

从创世区块开始,获取需要重新更新钱包交易的位置,然后开始扫描区块,以将相关交易加入到钱包中

继续看CreateWalletFromFile

...
// 获取创世区块
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false))

    WalletBatch batch(*walletInstance->database);
    CBlockLocator locator;
    if (batch.ReadBestBlock(locator))
        pindexRescan = FindForkInGlobalIndex(chainActive, locator);

// 获取最高块
walletInstance->m_last_block_processed = chainActive.Tip();

if (chainActive.Tip() && chainActive.Tip() != pindexRescan)

    ...
    // 如果区块在钱包创建之前就存在,则无需扫描
    while (pindexRescan && walletInstance->nTimeFirstKey && (pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) 
        pindexRescan = chainActive.Next(pindexRescan);
    
    ...
    // 扫描区块链,更新钱包中交易
    walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);   
	...
    // -zapwallettxes=1 : 重新加载钱包交易数据
    if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")  ... 

其中ScanForWalletTransactions: 扫描区块链,如果fUpdate为true,则更新钱包中交易

...
// 遍历区块
while (pindex && !fAbortRescan && !ShutdownRequested())

    ...
    CBlock block;
    if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) 
        LOCK2(cs_main, cs_wallet);
        if (pindex && !chainActive.Contains(pindex)) 
            ret = pindex;
            break;
        
        for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) 
            // 更新交易【todo: 这里需要再仔细看】
            SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
        
    
    ...

以上是关于区块链比特币学习 - 3 - 钱包的主要内容,如果未能解决你的问题,请参考以下文章

BTC比特币HD钱包开发教程1|简单知识

区块链精通比特币学习笔记

区块链比特币学习 - 2 - 密钥

区块链比特币学习 - 5 -创币交易

《区块链100问》第15集:比特币怎么转账?

区块链生存训练