OSDN Git Service

fix api
[bytom/bytom.git] / wallet / unconfirmed.go
index f932dff..5b04b1b 100644 (file)
@@ -6,21 +6,98 @@ import (
        "sort"
        "time"
 
+       log "github.com/sirupsen/logrus"
+
        "github.com/bytom/account"
        "github.com/bytom/blockchain/query"
        "github.com/bytom/crypto/sha3pool"
+       "github.com/bytom/protocol"
        "github.com/bytom/protocol/bc/types"
 )
 
 const (
        //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
-       UnconfirmedTxPrefix = "UTXS:"
+       UnconfirmedTxPrefix      = "UTXS:"
+       UnconfirmedTxCheckPeriod = 30 * time.Minute
+       MaxUnconfirmedTxDuration = 24 * time.Hour
 )
 
 func calcUnconfirmedTxKey(formatKey string) []byte {
        return []byte(UnconfirmedTxPrefix + formatKey)
 }
 
+// SortByTimestamp implements sort.Interface for AnnotatedTx slices
+type SortByTimestamp []*query.AnnotatedTx
+
+func (a SortByTimestamp) Len() int           { return len(a) }
+func (a SortByTimestamp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
+
+// SortByHeight implements sort.Interface for AnnotatedTx slices
+type SortByHeight []*query.AnnotatedTx
+
+func (a SortByHeight) Len() int           { return len(a) }
+func (a SortByHeight) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a SortByHeight) Less(i, j int) bool { return a[i].BlockHeight > a[j].BlockHeight }
+
+// AddUnconfirmedTx handle wallet status update when tx add into txpool
+func (w *Wallet) AddUnconfirmedTx(txD *protocol.TxDesc) {
+       if err := w.saveUnconfirmedTx(txD.Tx); err != nil {
+               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on saveUnconfirmedTx")
+       }
+
+       utxos := txOutToUtxos(txD.Tx, txD.StatusFail, 0)
+       utxos = w.filterAccountUtxo(utxos)
+       w.AccountMgr.AddUnconfirmedUtxo(utxos)
+}
+
+// GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
+func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
+       annotatedTxs := []*query.AnnotatedTx{}
+       txIter := w.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
+       defer txIter.Release()
+
+       for txIter.Next() {
+               annotatedTx := &query.AnnotatedTx{}
+               if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
+                       return nil, err
+               }
+
+               if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
+                       annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
+                       annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
+               }
+       }
+
+       sort.Sort(SortByTimestamp(annotatedTxs))
+       return annotatedTxs, nil
+}
+
+// GetUnconfirmedTxByTxID get unconfirmed transaction by txID
+func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
+       annotatedTx := &query.AnnotatedTx{}
+       txInfo := w.DB.Get(calcUnconfirmedTxKey(txID))
+       if txInfo == nil {
+               return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
+       }
+
+       if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
+               return nil, err
+       }
+
+       annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
+       return annotatedTx, nil
+}
+
+// RemoveUnconfirmedTx handle wallet status update when tx removed from txpool
+func (w *Wallet) RemoveUnconfirmedTx(txD *protocol.TxDesc) {
+       if !w.checkRelatedTransaction(txD.Tx) {
+               return
+       }
+       w.DB.Delete(calcUnconfirmedTxKey(txD.Tx.ID.String()))
+       w.AccountMgr.RemoveUnconfirmedUtxo(txD.Tx.ResultIds)
+}
+
 func (w *Wallet) buildAnnotatedUnconfirmedTx(tx *types.Tx) *query.AnnotatedTx {
        annotatedTx := &query.AnnotatedTx{
                ID:        tx.ID,
@@ -36,7 +113,6 @@ func (w *Wallet) buildAnnotatedUnconfirmedTx(tx *types.Tx) *query.AnnotatedTx {
        for i := range tx.Outputs {
                annotatedTx.Outputs = append(annotatedTx.Outputs, w.BuildAnnotatedOutput(tx, i))
        }
-
        return annotatedTx
 }
 
@@ -59,12 +135,11 @@ func (w *Wallet) checkRelatedTransaction(tx *types.Tx) bool {
                        return true
                }
        }
-
        return false
 }
 
 // SaveUnconfirmedTx save unconfirmed annotated transaction to the database
-func (w *Wallet) SaveUnconfirmedTx(tx *types.Tx) error {
+func (w *Wallet) saveUnconfirmedTx(tx *types.Tx) error {
        if !w.checkRelatedTransaction(tx) {
                return nil
        }
@@ -84,48 +159,31 @@ func (w *Wallet) SaveUnconfirmedTx(tx *types.Tx) error {
        return nil
 }
 
-// GetUnconfirmedTxByTxID get unconfirmed transaction by txID
-func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
-       annotatedTx := &query.AnnotatedTx{}
-       txInfo := w.DB.Get(calcUnconfirmedTxKey(txID))
-       if txInfo == nil {
-               return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
+func (w *Wallet) delExpiredTxs() error {
+       AnnotatedTx, err := w.GetUnconfirmedTxs("")
+       if err != nil {
+               return err
        }
-
-       if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
-               return nil, err
+       for _, tx := range AnnotatedTx {
+               if time.Now().After(time.Unix(int64(tx.Timestamp), 0).Add(MaxUnconfirmedTxDuration)) {
+                       w.DB.Delete(calcUnconfirmedTxKey(tx.ID.String()))
+               }
        }
-       annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
-
-       return annotatedTx, nil
+       return nil
 }
 
-// SortByTimestamp implements sort.Interface for AnnotatedTx slices
-type SortByTimestamp []*query.AnnotatedTx
-
-func (a SortByTimestamp) Len() int           { return len(a) }
-func (a SortByTimestamp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
-
-// GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
-func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
-       annotatedTxs := []*query.AnnotatedTx{}
-
-       txIter := w.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
-       defer txIter.Release()
-       for txIter.Next() {
-               annotatedTx := &query.AnnotatedTx{}
-               if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
-                       return nil, err
-               }
-
-               if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
-                       annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
-                       annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
+//delUnconfirmedTx periodically delete locally stored timeout did not confirm txs
+func (w *Wallet) delUnconfirmedTx() {
+       if err := w.delExpiredTxs(); err != nil {
+               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on delUnconfirmedTx")
+               return
+       }
+       ticker := time.NewTicker(UnconfirmedTxCheckPeriod)
+       defer ticker.Stop()
+       for {
+               <-ticker.C
+               if err := w.delExpiredTxs(); err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on delUnconfirmedTx")
                }
        }
-
-       // sort SortByTimestamp by timestamp
-       sort.Sort(SortByTimestamp(annotatedTxs))
-       return annotatedTxs, nil
 }