7 log "github.com/sirupsen/logrus"
8 "github.com/tendermint/tmlibs/db"
10 "github.com/bytom/account"
11 "github.com/bytom/asset"
12 "github.com/bytom/blockchain/pseudohsm"
13 "github.com/bytom/protocol"
14 "github.com/bytom/protocol/bc"
15 "github.com/bytom/protocol/bc/types"
23 var walletKey = []byte("walletInfo")
25 //StatusInfo is base valid block info to handle orphan block rollback
26 type StatusInfo struct {
33 //Wallet is related to storing account unspent outputs
38 AccountMgr *account.Manager
39 AssetReg *asset.Registry
42 rescanCh chan struct{}
45 //NewWallet return a new wallet instance
46 func NewWallet(walletDB db.DB, account *account.Manager, asset *asset.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain) (*Wallet, error) {
53 rescanCh: make(chan struct{}, 1),
56 if err := w.loadWalletInfo(); err != nil {
64 //GetWalletInfo return stored wallet info and nil,if error,
65 //return initial wallet info and err
66 func (w *Wallet) loadWalletInfo() error {
67 if rawWallet := w.DB.Get(walletKey); rawWallet != nil {
68 return json.Unmarshal(rawWallet, &w.status)
71 block, err := w.chain.GetBlockByHeight(0)
75 return w.AttachBlock(block)
78 func (w *Wallet) commitWalletInfo(batch db.Batch) error {
79 rawWallet, err := json.Marshal(w.status)
81 log.WithField("err", err).Error("save wallet info")
85 batch.Set(walletKey, rawWallet)
90 // AttachBlock attach a new block
91 func (w *Wallet) AttachBlock(block *types.Block) error {
95 if block.PreviousBlockHash != w.status.WorkHash {
96 log.Warn("wallet skip attachBlock due to status hash not equal to previous hash")
100 blockHash := block.Hash()
101 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
106 storeBatch := w.DB.NewBatch()
107 w.indexTransactions(storeBatch, block, txStatus)
108 w.attachUtxos(storeBatch, block, txStatus)
110 w.status.WorkHeight = block.Height
111 w.status.WorkHash = block.Hash()
112 if w.status.WorkHeight >= w.status.BestHeight {
113 w.status.BestHeight = w.status.WorkHeight
114 w.status.BestHash = w.status.WorkHash
116 return w.commitWalletInfo(storeBatch)
119 // DetachBlock detach a block and rollback state
120 func (w *Wallet) DetachBlock(block *types.Block) error {
124 blockHash := block.Hash()
125 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
130 storeBatch := w.DB.NewBatch()
131 w.detachUtxos(storeBatch, block, txStatus)
132 w.deleteTransactions(storeBatch, w.status.BestHeight)
134 w.status.BestHeight = block.Height - 1
135 w.status.BestHash = block.PreviousBlockHash
137 if w.status.WorkHeight > w.status.BestHeight {
138 w.status.WorkHeight = w.status.BestHeight
139 w.status.WorkHash = w.status.BestHash
142 return w.commitWalletInfo(storeBatch)
145 //WalletUpdate process every valid block and reverse every invalid block which need to rollback
146 func (w *Wallet) walletUpdater() {
148 w.getRescanNotification()
149 for !w.chain.InMainChain(w.status.BestHash) {
150 block, err := w.chain.GetBlockByHash(&w.status.BestHash)
152 log.WithField("err", err).Error("walletUpdater GetBlockByHash")
156 if err := w.DetachBlock(block); err != nil {
157 log.WithField("err", err).Error("walletUpdater detachBlock stop")
162 block, _ := w.chain.GetBlockByHeight(w.status.WorkHeight + 1)
164 w.walletBlockWaiter()
168 if err := w.AttachBlock(block); err != nil {
169 log.WithField("err", err).Error("walletUpdater AttachBlock stop")
175 //RescanBlocks provide a trigger to rescan blocks
176 func (w *Wallet) RescanBlocks() {
178 case w.rescanCh <- struct{}{}:
184 func (w *Wallet) getRescanNotification() {
193 func (w *Wallet) setRescanStatus() {
194 block, _ := w.chain.GetBlockByHeight(0)
195 w.status.WorkHash = bc.Hash{}
199 func (w *Wallet) walletBlockWaiter() {
201 case <-w.chain.BlockWaiter(w.status.WorkHeight + 1):
207 // GetWalletStatusInfo return current wallet StatusInfo
208 func (w *Wallet) GetWalletStatusInfo() StatusInfo {