package wallet
import (
- "encoding/json"
+ "encoding/hex"
"fmt"
"sort"
"time"
+ "github.com/bytom/vapor/protocol/bc"
+
log "github.com/sirupsen/logrus"
- "github.com/vapor/account"
- "github.com/vapor/blockchain/query"
- "github.com/vapor/crypto/sha3pool"
- "github.com/vapor/protocol"
- "github.com/vapor/protocol/bc/types"
+ acc "github.com/bytom/vapor/account"
+ "github.com/bytom/vapor/blockchain/query"
+ "github.com/bytom/vapor/crypto/sha3pool"
+ "github.com/bytom/vapor/protocol"
+ "github.com/bytom/vapor/protocol/bc/types"
)
const (
- //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
- 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) 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 {
// 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
- }
+ annotatedTxs, err := w.Store.ListUnconfirmedTransactions()
+ if err != nil {
+ return nil, err
+ }
- if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
+ newAnnotatedTxs := []*query.AnnotatedTx{}
+ for _, annotatedTx := range annotatedTxs {
+ if accountID == "" || FindTransactionsByAccount(annotatedTx, accountID) {
annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
- annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
+ newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
}
}
- sort.Sort(SortByTimestamp(annotatedTxs))
- return annotatedTxs, nil
+ sort.Sort(SortByTimestamp(newAnnotatedTxs))
+ return newAnnotatedTxs, 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 {
+ annotatedTx, err := w.Store.GetUnconfirmedTransaction(txID)
+ if err != nil {
return nil, err
}
+ if annotatedTx == nil {
+ return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
+ }
annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
return annotatedTx, nil
}
if !w.checkRelatedTransaction(txD.Tx) {
return
}
- w.DB.Delete(calcUnconfirmedTxKey(txD.Tx.ID.String()))
+
+ w.Store.DeleteUnconfirmedTransaction(txD.Tx.ID.String())
w.AccountMgr.RemoveUnconfirmedUtxo(txD.Tx.ResultIds)
}
for _, v := range tx.Outputs {
var hash [32]byte
sha3pool.Sum256(hash[:], v.ControlProgram())
- if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
+ cp, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash))
+ if err != nil && err != acc.ErrFindCtrlProgram {
+ log.WithFields(log.Fields{"module": logModule, "err": err, "hash": hex.EncodeToString(hash[:])}).Error("checkRelatedTransaction fail.")
+ continue
+ }
+ if cp != nil {
return true
}
}
if err != nil {
continue
}
- if bytes := w.DB.Get(account.StandardUTXOKey(outid)); bytes != nil {
+ utxo, err := w.Store.GetStandardUTXO(outid)
+ if err != nil && err != ErrGetStandardUTXO {
+ log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("checkRelatedTransaction fail.")
+ continue
+ }
+ if utxo != nil {
return true
}
}
annotatedTx := w.buildAnnotatedUnconfirmedTx(tx)
annotatedTxs := []*query.AnnotatedTx{}
annotatedTxs = append(annotatedTxs, annotatedTx)
- annotateTxsAccount(annotatedTxs, w.DB)
+ w.annotateTxsAccount(annotatedTxs)
- rawTx, err := json.Marshal(annotatedTxs[0])
- if err != nil {
+ if err := w.Store.SetUnconfirmedTransaction(tx.ID.String(), annotatedTxs[0]); err != nil {
return err
}
-
- w.DB.Set(calcUnconfirmedTxKey(tx.ID.String()), rawTx)
return nil
}
if err != nil {
return err
}
+
for _, tx := range AnnotatedTx {
if time.Now().After(time.Unix(int64(tx.Timestamp), 0).Add(MaxUnconfirmedTxDuration)) {
- w.DB.Delete(calcUnconfirmedTxKey(tx.ID.String()))
+ w.Store.DeleteUnconfirmedTransaction(tx.ID.String())
}
}
return 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 {