utxo的刷盘逻辑主要在txdb.cpp
中实现,主要是 CoinsViewDB::batchwrite
这个函数。下面我们来分析一下:
1 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { |
在前面我们介绍过 CDBWrapper
主要是对 leveldb的一个简单封装,定义一个CDBWrapper db;
我们拿着 db 就可以实现相应的操作。
接下来迭代mapCoins,并填充其值,这里最主要的就是作为k-v数据库的leveldb中的key与value如何获得:
key
CoinEntry是一个辅助工具类。1
2
3
4
5
6
7
8
9
10
11
12struct CoinEntry {
COutPoint *outpoint;
char key;
CoinEntry(const COutPoint *ptr)
: outpoint(const_cast<COutPoint *>(ptr)), key(DB_COIN) {}
template <typename Stream> void Serialize(Stream &s) const {
s << key;
s << outpoint->hash;
s << VARINT(outpoint->n);
}
};
key指向的是outpoint,具体结构如下:
我们将序列化后的值当作key,作为entry的参数,同时作为db.write
的key。
关于db.write和db.WriteBatch二者之间的联系,前面已经详细分析。
value
value的值就是 coin 序列化后的值,而 coin 又包含了txout,如下:
1
2
3
4
5
6
7 class Coin {
//! Unspent transaction output.
CTxOut out;
//! Whether containing transaction was a coinbase and height at which the
//! transaction was included into a block.
uint32_t nHeightAndIsCoinBase;
同样的,我们进行序列化并使用CTxOutCompressor
对txout进行压缩,REF是一个宏定义,是非const转换,我们首先断言这个币是否被消费:1
2
3
4
5template <typename Stream> void Serialize(Stream &s) const {
assert(!IsSpent());
::Serialize(s, VARINT(nHeightAndIsCoinBase));
::Serialize(s, CTxOutCompressor(REF(out)));
}
txout主要包含:1
2
3
4class CTxOut {
public:
Amount nValue;
CScript scriptPubKey;
对nValue和scriptPubKey采用了不同的压缩方式来进行序列化,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class CTxOutCompressor {
private:
CTxOut &txout;
public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
if (!ser_action.ForRead()) {
uint64_t nVal = CompressAmount(txout.nValue);
READWRITE(VARINT(nVal));
} else {
uint64_t nVal = 0;
READWRITE(VARINT(nVal));
txout.nValue = DecompressAmount(nVal);
}
CScriptCompressor cscript(REF(txout.scriptPubKey));
READWRITE(cscript);
}
};
这时候我们就拿到了db.write
的value值,这时候我们通过for循环,不断迭代,将值写入磁盘。