【区块链】比特币学习 - 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);
}
}
...
}