Skip to content
On this page

wtxmgr模块

功能

  • Storage for relevant wallet transactions
  • Ability to mark outputs as controlled by wallet
  • Unspent transaction output index
  • Balance tracking
  • Automatic spend tracking for transaction inserts and removals
  • Double spend detection and correction after blockchain reorgs
  • Scalable design:
    • Utilizes similar prefixes to allow cursor iteration over relevant transaction inputs and outputs
    • Programmatically detectable errors, including encapsulation of errors from packages it relies on
    • Operates under its own walletdb namespace

db 设计

bucketBlocks         = []byte("b")
bucketTxRecords      = []byte("t")
bucketCredits        = []byte("c")
bucketUnspent        = []byte("u")
bucketDebits         = []byte("d")
bucketUnmined        = []byte("m")
bucketUnminedCredits = []byte("mc")
bucketUnminedInputs  = []byte("mi")
  1. bucketBlocks 存储某个块有哪些Tx,没有考虑分叉 bucketBlocks: blockNumber=>blockHash+blockTime+TxCount+[ TxHash1,TxHash2...]

  2. bucketTxRecords 存储序列化的Tx,已经被打包上链的, TxHash+blockNumber+blockHash=>SerializedTx

  3. bucketCredits 存储未花费的UTXO,或者已花费,但是还没有确认的,这些都是我关注的 Txhash+blockNumber+blockHash+Index(outpoint中)=>UTXO Amount[8个字节]+其他信息 其他信息: v[8]第0位表示是否已消费 1 表示已消费 v[8]第1位表示是否是找零 1 为找零 如果已经消费,那么第9个字节后还会有TxHash+blockNumber+blockHash+Index 表示这个UTXO在哪里被消费了.

  4. bucketUnspent 存储需要我关注的未消费的UTXO,一旦该UTXO被消费,就会删除相关记录 存储outPoint=>blockNumer+blocHash 该outpoint产生的block

  5. bucketDebits 这个需要解释清楚 记录钱包中一笔被消费的UTXO, debit啥意思呢 Txhash+blockNumber+blockHash+Index(outpoint中)=>Amount[8字节]+Txhash+blockNumber+blockHash+Index

  6. bucketUnmined 存储进入memPool,但是还未被打包的交易 TxHash=>ReceivedTime(8字节)+SeralizedTx

  7. bucketUnminedCredits 存储那些Tx输出是到我的钱包地址的OutPoint,并且这些Tx还未被打包 outpoint=>UTXO Amount+change 参考bucketCredits

  8. bucketUnminedInputs 保存已经消费的UTXO,但是还未被打包或者正在被打包 这些UTXO已经被进入mempool的Tx消费了. outpoint=>[TxHash1,TxHash2] TxHash1,TxHash2可能会消费这个outpoint

store模块

store是wallet与数据库打交道的接口,通过store来管理Tx以及Balance.

InsertTx

rec表示当前新收到的Tx, block不为空则是Tx所在块,空表示来自mempool的tx

go
func (s *Store) InsertTx(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) error

来自memPool的Tx

对于来自MemPool中的Tx,则block为空,

  1. 首先将Tx放入bucketUnmined TxHash=>ReceivedTime(8字节)+SeralizedTx
  2. 然后将Tx中的每一个TxIn放入bucketUnminedInputs outpoint=>[TxHash1,TxHash2]

来自链上的Tx

对于来自于链上的Tx,有block信息

  1. 将Tx放入 bucketBlocks
  2. 放入bucketTxRecords,3-4步骤为updateMinedBalance
  3. 如果该Tx的output在bucketUnminedCredits中有记录,那么将其放入bucketCredits和bucketUnspent
  4. 更新Balance,因为在步骤三种增加了Balance.
  5. 如果bucketUnmined有这个Tx的记录,调用removeConflict删除
  6. 根据bucketUnminedInputs,搜索哪些bucketUnmined中的Tx(doubleSpendTx)和当前Tx产生了双花的,从bucketUnmined和bucketUnminedInputs删除这些记录
  7. 如果有其他在bucketUnminedInputs引用了该doubleSpendTx中的输出,则进行级联删除,因此removeConflict是递归调用.

AddCredit 添加可消费的UTXO

收到了一个我关注的Tx,其中某个Output的地址在我的钱包之中,这时候才调用AddCredit rec: 其中某个输出是给我的那个Tx index:第几个输出是给我的 block:包含这个rec的块 change: Internal returns true if the backing address was created for internal use such as a change output of a transaction. 我的理解就是找零地址 返回值

go
func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) error 

来自mempool的Tx

block 是nil表示来自mempool

  1. 如果outpoint已经在bucketUnminedCredits中了,直接结束
  2. 如果outpoint已经在bucketUnspent,直接结束
  3. 将这个outpoint放入bucketUnminedCredits

来自链上的Tx

block 表示包含该Tx的块信息

  1. 如果bucketCredits包含该outpoint,结束
  2. 在bucketCredits记录该outpoint
  3. 调用putMinedBalance增加该outpoint的Amount到Balance
  4. 将该outpoint放入bucketUnspent

InsertTx和AddCredit的调用关系 一般是InsertTx(memPool),addCredit(mempool),InsertTx(block!=nil),addCredit(block!=nil)

Rollback函数

当发生块重组的时候需要rollback

go
// Rollback removes all blocks at height onwards, moving any transactions within
// each block to the unconfirmed pool.
func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error {
	return s.rollback(ns, height)
}

这个函数非常复杂, 深入此函数也可以看出链重组的时候相关的数据应该如何处理. 相比与以太坊的StateTransition模型,这里要复杂很多.

  1. 首先按照块高度倒着遍历每一块
  2. 针对当前块中的每一个Tx进行操作
  3. 从bucketTxRecords中删除该Tx
  4. 如果是coinbaseTx,从bucketUnspent和bucketCredits删除相关记录,然后回到2. 如果coinbase已经被消费,则参考9
  5. 把Tx返回bucketUnmined
  6. 遍历Tx的每一个TxIn,在bucketUnminedInputs重新记录依赖关系 6.1 如果bucketDebits中有记录,则表示是我的一个UTXO被消费了,需要撤销 6.2 重新将该UTXO从bucketDebits删除, 6.3 将该UTXO重新放回bucketUnspent, 我的问题,bucketCredits的记录怎么办? 6.4 minedBalance重新加回去
  7. 遍历Tx的每一个TxOut 7.1 如果Txout是我的钱包地址,则将该TxOut放回bucketUnminedCredits 7.2 删除bucketCredits中的记录 7.3 删除bucketUnspent的记录 7.4 minedBalance重新减回去
  8. 1-7循环结束以后,从bucketBlocks删除Block记录
  9. 遍历coinBaseCredits(来自于步骤4) 将所有依赖该Tx的bucketUnminedInputs和bucketUnminedCredits中的记录全删除 调用removeConflict递归删除依赖 不明白的是对于那些已经上链的交易的交易怎么处理? 也就是CoinBase->Tx1->Tx2,Tx2已经上链了