本文讲解钱包收到区块链发来的交易通知,同步交易的操作:
打开钱包,或者创建钱包之后,如果连接的区块链客户端不为空,则会调用SynchronizeRPC接口,准备接受请求。
比如OpenWallet的源码
func (s *loaderServer) OpenWallet(ctx context.Context, req *pb.OpenWalletRequest) (
*pb.OpenWalletResponse, error) {
// Use an insecure public passphrase when the request's is empty.
pubPassphrase := req.PublicPassphrase
if len(pubPassphrase) == 0 {
pubPassphrase = []byte(wallet.InsecurePubPassphrase)
}
wallet, err := s.loader.OpenExistingWallet(pubPassphrase, false)
if err != nil {
return nil, translateError(err)
}
s.mu.Lock()
if s.rpcClient != nil {
// 打开同步RPC的接口。
wallet.SynchronizeRPC(s.rpcClient)
}
s.mu.Unlock()
return &pb.OpenWalletResponse{}, nil
}
在SynchronizeRPC 中启动了多个gorotine。
go w.handleChainNotifications() // 处理来自区块链的消息,包括区块、交易的变换。
go w.rescanBatchHandler() //
go w.rescanProgressHandler()
go w.rescanRPCHandler() //
handleChainNotifications 中通知消息分了如下几种:(跟本文无关的代码省略了,只保留其对应的分支)
func (w *Wallet) handleChainNotifications() {
defer w.wg.Done()
log.Info("Welcome to zhangpeng's btcwallet , rpcClientConnectLoop in SynchronizeRPC, handleChainNotifications")
chainClient, err := w.requireChainClient()
if err != nil {
log.Errorf("handleChainNotifications called without RPC client")
return
}
for {
select {
case n, ok := <-chainClient.Notifications():
if !ok {
return
}
var notificationName string
var err error
switch n := n.(type) {
case chain.ClientConnected:
// 来自chain的连接请求。
...
case chain.BlockConnected:
// 区块已经连接的通知。
...
notificationName = "block connected"
case chain.BlockDisconnected:
// 区块断开连接的通知
...
case chain.RelevantTx:
// 区块相关交易的通知。
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.addRelevantTx(tx, n.TxRecord, n.Block)
})
notificationName = "relevant transaction"
case chain.FilteredBlockConnected:
// 区块新产生,多个交易
// Atomically update for the whole block.
if len(n.RelevantTxs) > 0 {
err = walletdb.Update(w.db, func(
tx walletdb.ReadWriteTx) error {
var err error
for _, rec := range n.RelevantTxs {
err = w.addRelevantTx(tx, rec,
n.Block)
if err != nil {
return err
}
}
return nil
})
}
notificationName = "filtered block connected"
// The following require some database maintenance, but also
// need to be reported to the wallet's rescan goroutine.
case *chain.RescanProgress:
// rescan正在执行中的通知。一般发生在钱包刚刚连接区块链节点的时候
...
case *chain.RescanFinished:
// rescan执行完成的通知。
...
}
...
case <-w.quit:
return
}
}
}
当区块产生的时候,会发送 FilteredBlockConnected 消息。下面看一下FilteredBlockConnected 的流程。
case chain.FilteredBlockConnected:
fmt.Println("zp wallet, 消息通知。。。。整个区块")
// Atomically update for the whole block.
if len(n.RelevantTxs) > 0 {
err = walletdb.Update(w.db, func(
tx walletdb.ReadWriteTx) error {
var err error
for _, rec := range n.RelevantTxs {
err = w.addRelevantTx(tx, rec,
n.Block)
if err != nil {
return err
}
}
return nil
})
}
notificationName = "filtered block connected"
里面最核心的方法是 addRelevantTx。 下面我们看一下其流程: https://github.com/btcsuite/btcwallet/blob/7abdd4f8ad7dcf22c5dd90d123b0c137f93d3879/wallet/chainntfns.go#L275
addRelevantTx.png本文是《循序渐进比特币》的第十一篇-《btcwallet(五)接收区块,同步本地交易与UTXO》。
如果有疑问,可以直接留言,也可以关注公众号 “链人成长chainerup” 提问留言,或者加入知识星球“链人成长”~
网友评论