6 log "github.com/sirupsen/logrus"
7 "github.com/tendermint/tmlibs/db"
9 "github.com/bytom/account"
10 "github.com/bytom/asset"
11 "github.com/bytom/blockchain/pseudohsm"
12 "github.com/bytom/protocol"
13 "github.com/bytom/protocol/bc"
14 "github.com/bytom/protocol/bc/types"
20 var walletKey = []byte("walletInfo")
22 //StatusInfo is base valid block info to handle orphan block rollback
23 type StatusInfo struct {
30 //Wallet is related to storing account unspent outputs
34 AccountMgr *account.Manager
35 AssetReg *asset.Registry
38 rescanCh chan struct{}
41 //NewWallet return a new wallet instance
42 func NewWallet(walletDB db.DB, account *account.Manager, asset *asset.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain) (*Wallet, error) {
49 rescanCh: make(chan struct{}, 1),
52 if err := w.loadWalletInfo(); err != nil {
61 //GetWalletInfo return stored wallet info and nil,if error,
62 //return initial wallet info and err
63 func (w *Wallet) loadWalletInfo() error {
64 if rawWallet := w.DB.Get(walletKey); rawWallet != nil {
65 return json.Unmarshal(rawWallet, &w.status)
68 block, err := w.chain.GetBlockByHeight(0)
72 return w.AttachBlock(block)
75 func (w *Wallet) commitWalletInfo(batch db.Batch) error {
76 rawWallet, err := json.Marshal(w.status)
78 log.WithField("err", err).Error("save wallet info")
82 batch.Set(walletKey, rawWallet)
87 // AttachBlock attach a new block
88 func (w *Wallet) AttachBlock(block *types.Block) error {
89 if block.PreviousBlockHash != w.status.WorkHash {
90 log.Warn("wallet skip attachBlock due to status hash not equal to previous hash")
94 blockHash := block.Hash()
95 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
100 storeBatch := w.DB.NewBatch()
101 w.indexTransactions(storeBatch, block, txStatus)
102 w.buildAccountUTXOs(storeBatch, block, txStatus)
104 w.status.WorkHeight = block.Height
105 w.status.WorkHash = block.Hash()
106 if w.status.WorkHeight >= w.status.BestHeight {
107 w.status.BestHeight = w.status.WorkHeight
108 w.status.BestHash = w.status.WorkHash
110 return w.commitWalletInfo(storeBatch)
113 // DetachBlock detach a block and rollback state
114 func (w *Wallet) DetachBlock(block *types.Block) error {
115 blockHash := block.Hash()
116 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
121 storeBatch := w.DB.NewBatch()
122 w.reverseAccountUTXOs(storeBatch, block, txStatus)
123 w.deleteTransactions(storeBatch, w.status.BestHeight)
125 w.status.BestHeight = block.Height - 1
126 w.status.BestHash = block.PreviousBlockHash
128 if w.status.WorkHeight > w.status.BestHeight {
129 w.status.WorkHeight = w.status.BestHeight
130 w.status.WorkHash = w.status.BestHash
133 return w.commitWalletInfo(storeBatch)
136 //WalletUpdate process every valid block and reverse every invalid block which need to rollback
137 func (w *Wallet) walletUpdater() {
139 w.getRescanNotification()
140 for !w.chain.InMainChain(w.status.BestHash) {
141 block, err := w.chain.GetBlockByHash(&w.status.BestHash)
143 log.WithField("err", err).Error("walletUpdater GetBlockByHash")
147 if err := w.DetachBlock(block); err != nil {
148 log.WithField("err", err).Error("walletUpdater detachBlock")
153 block, _ := w.chain.GetBlockByHeight(w.status.WorkHeight + 1)
155 <-w.chain.BlockWaiter(w.status.WorkHeight + 1)
159 if err := w.AttachBlock(block); err != nil {
160 log.WithField("err", err).Error("walletUpdater stop")
166 func (w *Wallet) RescanBlocks() {
168 case w.rescanCh <- struct{}{}:
174 func (w *Wallet) getRescanNotification() {
177 block, _ := w.chain.GetBlockByHeight(0)
178 w.status.WorkHash = bc.Hash{}
185 func (w *Wallet) createProgram(account *account.Account, XPub *pseudohsm.XPub, index uint64) error {
186 for i := uint64(0); i < index; i++ {
187 if _, err := w.AccountMgr.CreateAddress(nil, account.ID, false); err != nil {