wtxmgr模块

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

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. 我的理解就是找零地址
返回值

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的块信息

  4. 如果bucketCredits包含该outpoint,结束

  5. 在bucketCredits记录该outpoint

  6. 调用putMinedBalance增加该outpoint的Amount到Balance

  7. 将该outpoint放入bucketUnspent

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

Rollback函数

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

// 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已经上链了