9 log "github.com/sirupsen/logrus"
11 "github.com/vapor/blockchain/query"
12 "github.com/vapor/crypto/sha3pool"
13 "github.com/vapor/protocol"
14 "github.com/vapor/protocol/bc/types"
18 //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
19 UnconfirmedTxPrefix = "UTXS:"
20 UnconfirmedTxCheckPeriod = 30 * time.Minute
21 MaxUnconfirmedTxDuration = 24 * time.Hour
24 func calcUnconfirmedTxKey(formatKey string) []byte {
25 return []byte(UnconfirmedTxPrefix + formatKey)
28 // SortByTimestamp implements sort.Interface for AnnotatedTx slices
29 type SortByTimestamp []*query.AnnotatedTx
31 func (a SortByTimestamp) Len() int { return len(a) }
32 func (a SortByTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
33 func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
35 // AddUnconfirmedTx handle wallet status update when tx add into txpool
36 func (w *Wallet) AddUnconfirmedTx(txD *protocol.TxDesc) {
37 if err := w.saveUnconfirmedTx(txD.Tx); err != nil {
38 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on saveUnconfirmedTx")
41 utxos := txOutToUtxos(txD.Tx, txD.StatusFail, 0)
42 utxos = w.filterAccountUtxo(utxos)
43 w.AccountMgr.AddUnconfirmedUtxo(utxos)
46 // GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
47 func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
48 annotatedTxs := []*query.AnnotatedTx{}
49 annotatedTxs, err := w.store.GetUnconfirmedTransactions()
54 newAnnotatedTxs := []*query.AnnotatedTx{}
55 for _, annotatedTx := range annotatedTxs {
56 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
57 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
58 newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
62 sort.Sort(SortByTimestamp(newAnnotatedTxs))
63 return newAnnotatedTxs, nil
66 // GetUnconfirmedTxByTxID get unconfirmed transaction by txID
67 func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
68 annotatedTx := &query.AnnotatedTx{}
69 txInfo := w.store.GetUnconfirmedTransaction(txID)
71 return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
74 if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
78 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
79 return annotatedTx, nil
82 // RemoveUnconfirmedTx handle wallet status update when tx removed from txpool
83 func (w *Wallet) RemoveUnconfirmedTx(txD *protocol.TxDesc) {
84 if !w.checkRelatedTransaction(txD.Tx) {
87 w.store.DeleteUnconfirmedTransaction(txD.Tx.ID.String())
88 w.AccountMgr.RemoveUnconfirmedUtxo(txD.Tx.ResultIds)
91 func (w *Wallet) buildAnnotatedUnconfirmedTx(tx *types.Tx) *query.AnnotatedTx {
92 annotatedTx := &query.AnnotatedTx{
94 Timestamp: uint64(time.Now().UnixNano() / int64(time.Millisecond)),
95 Inputs: make([]*query.AnnotatedInput, 0, len(tx.Inputs)),
96 Outputs: make([]*query.AnnotatedOutput, 0, len(tx.Outputs)),
97 Size: tx.SerializedSize,
100 for i := range tx.Inputs {
101 annotatedTx.Inputs = append(annotatedTx.Inputs, w.BuildAnnotatedInput(tx, uint32(i)))
103 for i := range tx.Outputs {
104 annotatedTx.Outputs = append(annotatedTx.Outputs, w.BuildAnnotatedOutput(tx, i))
109 // checkRelatedTransaction check related unconfirmed transaction.
110 func (w *Wallet) checkRelatedTransaction(tx *types.Tx) bool {
111 for _, v := range tx.Outputs {
113 sha3pool.Sum256(hash[:], v.ControlProgram())
114 if bytes := w.store.GetRawProgram(hash); bytes != nil {
119 for _, v := range tx.Inputs {
120 outid, err := v.SpentOutputID()
124 if bytes := w.store.GetStandardUTXO(outid); bytes != nil {
131 // SaveUnconfirmedTx save unconfirmed annotated transaction to the database
132 func (w *Wallet) saveUnconfirmedTx(tx *types.Tx) error {
133 if !w.checkRelatedTransaction(tx) {
137 // annotate account and asset
138 annotatedTx := w.buildAnnotatedUnconfirmedTx(tx)
139 annotatedTxs := []*query.AnnotatedTx{}
140 annotatedTxs = append(annotatedTxs, annotatedTx)
141 annotateTxsAccount(annotatedTxs, w.store)
143 rawTx, err := json.Marshal(annotatedTxs[0])
148 w.store.SetUnconfirmedTransaction(tx.ID.String(), rawTx)
152 func (w *Wallet) delExpiredTxs() error {
153 AnnotatedTx, err := w.GetUnconfirmedTxs("")
157 for _, tx := range AnnotatedTx {
158 if time.Now().After(time.Unix(int64(tx.Timestamp), 0).Add(MaxUnconfirmedTxDuration)) {
159 w.store.DeleteUnconfirmedTransaction(tx.ID.String())
165 //delUnconfirmedTx periodically delete locally stored timeout did not confirm txs
166 func (w *Wallet) delUnconfirmedTx() {
167 if err := w.delExpiredTxs(); err != nil {
168 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on delUnconfirmedTx")
171 ticker := time.NewTicker(UnconfirmedTxCheckPeriod)
175 if err := w.delExpiredTxs(); err != nil {
176 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("wallet fail on delUnconfirmedTx")