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"
22 maxTxChanSize = 10000 // txChanSize is the size of channel listening to Txpool newTxCh
25 var walletKey = []byte("walletInfo")
27 //StatusInfo is base valid block info to handle orphan block rollback
28 type StatusInfo struct {
35 //Wallet is related to storing account unspent outputs
40 AccountMgr *account.Manager
41 AssetReg *asset.Registry
44 rescanCh chan struct{}
45 newTxCh chan *types.Tx
48 //NewWallet return a new wallet instance
49 func NewWallet(walletDB db.DB, account *account.Manager, asset *asset.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain) (*Wallet, error) {
56 rescanCh: make(chan struct{}, 1),
57 newTxCh: make(chan *types.Tx, maxTxChanSize),
60 if err := w.loadWalletInfo(); err != nil {
65 go w.UnconfirmedTxCollector()
70 //GetWalletInfo return stored wallet info and nil,if error,
71 //return initial wallet info and err
72 func (w *Wallet) loadWalletInfo() error {
73 if rawWallet := w.DB.Get(walletKey); rawWallet != nil {
74 return json.Unmarshal(rawWallet, &w.status)
77 block, err := w.chain.GetBlockByHeight(0)
81 return w.AttachBlock(block)
84 func (w *Wallet) commitWalletInfo(batch db.Batch) error {
85 rawWallet, err := json.Marshal(w.status)
87 log.WithField("err", err).Error("save wallet info")
91 batch.Set(walletKey, rawWallet)
96 // AttachBlock attach a new block
97 func (w *Wallet) AttachBlock(block *types.Block) error {
101 if block.PreviousBlockHash != w.status.WorkHash {
102 log.Warn("wallet skip attachBlock due to status hash not equal to previous hash")
106 blockHash := block.Hash()
107 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
112 storeBatch := w.DB.NewBatch()
113 w.indexTransactions(storeBatch, block, txStatus)
114 w.buildAccountUTXOs(storeBatch, block, txStatus)
116 w.status.WorkHeight = block.Height
117 w.status.WorkHash = block.Hash()
118 if w.status.WorkHeight >= w.status.BestHeight {
119 w.status.BestHeight = w.status.WorkHeight
120 w.status.BestHash = w.status.WorkHash
122 return w.commitWalletInfo(storeBatch)
125 // DetachBlock detach a block and rollback state
126 func (w *Wallet) DetachBlock(block *types.Block) error {
130 blockHash := block.Hash()
131 txStatus, err := w.chain.GetTransactionStatus(&blockHash)
136 storeBatch := w.DB.NewBatch()
137 w.reverseAccountUTXOs(storeBatch, block, txStatus)
138 w.deleteTransactions(storeBatch, w.status.BestHeight)
140 w.status.BestHeight = block.Height - 1
141 w.status.BestHash = block.PreviousBlockHash
143 if w.status.WorkHeight > w.status.BestHeight {
144 w.status.WorkHeight = w.status.BestHeight
145 w.status.WorkHash = w.status.BestHash
148 return w.commitWalletInfo(storeBatch)
151 //WalletUpdate process every valid block and reverse every invalid block which need to rollback
152 func (w *Wallet) walletUpdater() {
154 w.getRescanNotification()
155 for !w.chain.InMainChain(w.status.BestHash) {
156 block, err := w.chain.GetBlockByHash(&w.status.BestHash)
158 log.WithField("err", err).Error("walletUpdater GetBlockByHash")
162 if err := w.DetachBlock(block); err != nil {
163 log.WithField("err", err).Error("walletUpdater detachBlock stop")
168 block, _ := w.chain.GetBlockByHeight(w.status.WorkHeight + 1)
170 w.walletBlockWaiter()
174 if err := w.AttachBlock(block); err != nil {
175 log.WithField("err", err).Error("walletUpdater AttachBlock stop")
181 //RescanBlocks provide a trigger to rescan blocks
182 func (w *Wallet) RescanBlocks() {
184 case w.rescanCh <- struct{}{}:
190 func (w *Wallet) getRescanNotification() {
199 func (w *Wallet) setRescanStatus() {
200 block, _ := w.chain.GetBlockByHeight(0)
201 w.status.WorkHash = bc.Hash{}
205 func (w *Wallet) walletBlockWaiter() {
207 case <-w.chain.BlockWaiter(w.status.WorkHeight + 1):
213 // GetNewTxCh return a unconfirmed transaction feed channel
214 func (w *Wallet) GetNewTxCh() chan *types.Tx {
218 // UnconfirmedTxCollector collector unconfirmed transaction
219 func (w *Wallet) UnconfirmedTxCollector() {
221 w.SaveUnconfirmedTx(<-w.newTxCh)
225 // GetWalletStatusInfo return current wallet StatusInfo
226 func (w *Wallet) GetWalletStatusInfo() StatusInfo {
233 func (w *Wallet) createProgram(account *account.Account, XPub *pseudohsm.XPub, index uint64) error {
234 for i := uint64(0); i < index; i++ {
235 if _, err := w.AccountMgr.CreateAddress(nil, account.ID, false); err != nil {