区块链比特币学习 - 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 - 钱包的主要内容,如果未能解决你的问题,请参考以下文章