Appearance
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")
bucketBlocks 存储某个块有哪些Tx,没有考虑分叉 bucketBlocks: blockNumber=>blockHash+blockTime+TxCount+[ TxHash1,TxHash2...]
bucketTxRecords 存储序列化的Tx,已经被打包上链的, TxHash+blockNumber+blockHash=>SerializedTx
bucketCredits 存储未花费的UTXO,或者已花费,但是还没有确认的,这些都是我关注的 Txhash+blockNumber+blockHash+Index(outpoint中)=>UTXO Amount[8个字节]+其他信息 其他信息: v[8]第0位表示是否已消费 1 表示已消费 v[8]第1位表示是否是找零 1 为找零 如果已经消费,那么第9个字节后还会有TxHash+blockNumber+blockHash+Index 表示这个UTXO在哪里被消费了.
bucketUnspent 存储需要我关注的未消费的UTXO,一旦该UTXO被消费,就会删除相关记录 存储outPoint=>blockNumer+blocHash 该outpoint产生的block
bucketDebits 这个需要解释清楚 记录钱包中一笔被消费的UTXO, debit啥意思呢 Txhash+blockNumber+blockHash+Index(outpoint中)=>Amount[8字节]+Txhash+blockNumber+blockHash+Index
bucketUnmined 存储进入memPool,但是还未被打包的交易 TxHash=>ReceivedTime(8字节)+SeralizedTx
bucketUnminedCredits 存储那些Tx输出是到我的钱包地址的OutPoint,并且这些Tx还未被打包 outpoint=>UTXO Amount+change 参考bucketCredits
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为空,
- 首先将Tx放入bucketUnmined TxHash=>ReceivedTime(8字节)+SeralizedTx
- 然后将Tx中的每一个TxIn放入bucketUnminedInputs outpoint=>[TxHash1,TxHash2]
来自链上的Tx
对于来自于链上的Tx,有block信息
- 将Tx放入 bucketBlocks
- 放入bucketTxRecords,3-4步骤为updateMinedBalance
- 如果该Tx的output在bucketUnminedCredits中有记录,那么将其放入bucketCredits和bucketUnspent
- 更新Balance,因为在步骤三种增加了Balance.
- 如果bucketUnmined有这个Tx的记录,调用removeConflict删除
- 根据bucketUnminedInputs,搜索哪些bucketUnmined中的Tx(doubleSpendTx)和当前Tx产生了双花的,从bucketUnmined和bucketUnminedInputs删除这些记录
- 如果有其他在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
- 如果outpoint已经在bucketUnminedCredits中了,直接结束
- 如果outpoint已经在bucketUnspent,直接结束
- 将这个outpoint放入bucketUnminedCredits
来自链上的Tx
block 表示包含该Tx的块信息
- 如果bucketCredits包含该outpoint,结束
- 在bucketCredits记录该outpoint
- 调用putMinedBalance增加该outpoint的Amount到Balance
- 将该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模型,这里要复杂很多.
- 首先按照块高度倒着遍历每一块
- 针对当前块中的每一个Tx进行操作
- 从bucketTxRecords中删除该Tx
- 如果是coinbaseTx,从bucketUnspent和bucketCredits删除相关记录,然后回到2. 如果coinbase已经被消费,则参考9
- 把Tx返回bucketUnmined
- 遍历Tx的每一个TxIn,在bucketUnminedInputs重新记录依赖关系 6.1 如果bucketDebits中有记录,则表示是我的一个UTXO被消费了,需要撤销 6.2 重新将该UTXO从bucketDebits删除, 6.3 将该UTXO重新放回bucketUnspent, 我的问题,bucketCredits的记录怎么办? 6.4 minedBalance重新加回去
- 遍历Tx的每一个TxOut 7.1 如果Txout是我的钱包地址,则将该TxOut放回bucketUnminedCredits 7.2 删除bucketCredits中的记录 7.3 删除bucketUnspent的记录 7.4 minedBalance重新减回去
- 1-7循环结束以后,从bucketBlocks删除Block记录
- 遍历coinBaseCredits(来自于步骤4) 将所有依赖该Tx的bucketUnminedInputs和bucketUnminedCredits中的记录全删除 调用removeConflict递归删除依赖 不明白的是对于那些已经上链的交易的交易怎么处理? 也就是CoinBase->Tx1->Tx2,Tx2已经上链了