OSDN Git Service

f932dffa39f49ba12e940347245dbeaa61169f2d
[bytom/bytom.git] / wallet / unconfirmed.go
1 package wallet
2
3 import (
4         "encoding/json"
5         "fmt"
6         "sort"
7         "time"
8
9         "github.com/bytom/account"
10         "github.com/bytom/blockchain/query"
11         "github.com/bytom/crypto/sha3pool"
12         "github.com/bytom/protocol/bc/types"
13 )
14
15 const (
16         //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
17         UnconfirmedTxPrefix = "UTXS:"
18 )
19
20 func calcUnconfirmedTxKey(formatKey string) []byte {
21         return []byte(UnconfirmedTxPrefix + formatKey)
22 }
23
24 func (w *Wallet) buildAnnotatedUnconfirmedTx(tx *types.Tx) *query.AnnotatedTx {
25         annotatedTx := &query.AnnotatedTx{
26                 ID:        tx.ID,
27                 Timestamp: uint64(time.Now().Unix()),
28                 Inputs:    make([]*query.AnnotatedInput, 0, len(tx.Inputs)),
29                 Outputs:   make([]*query.AnnotatedOutput, 0, len(tx.Outputs)),
30                 Size:      tx.SerializedSize,
31         }
32
33         for i := range tx.Inputs {
34                 annotatedTx.Inputs = append(annotatedTx.Inputs, w.BuildAnnotatedInput(tx, uint32(i)))
35         }
36         for i := range tx.Outputs {
37                 annotatedTx.Outputs = append(annotatedTx.Outputs, w.BuildAnnotatedOutput(tx, i))
38         }
39
40         return annotatedTx
41 }
42
43 // checkRelatedTransaction check related unconfirmed transaction.
44 func (w *Wallet) checkRelatedTransaction(tx *types.Tx) bool {
45         for _, v := range tx.Outputs {
46                 var hash [32]byte
47                 sha3pool.Sum256(hash[:], v.ControlProgram)
48                 if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
49                         return true
50                 }
51         }
52
53         for _, v := range tx.Inputs {
54                 outid, err := v.SpentOutputID()
55                 if err != nil {
56                         continue
57                 }
58                 if bytes := w.DB.Get(account.StandardUTXOKey(outid)); bytes != nil {
59                         return true
60                 }
61         }
62
63         return false
64 }
65
66 // SaveUnconfirmedTx save unconfirmed annotated transaction to the database
67 func (w *Wallet) SaveUnconfirmedTx(tx *types.Tx) error {
68         if !w.checkRelatedTransaction(tx) {
69                 return nil
70         }
71
72         // annotate account and asset
73         annotatedTx := w.buildAnnotatedUnconfirmedTx(tx)
74         annotatedTxs := []*query.AnnotatedTx{}
75         annotatedTxs = append(annotatedTxs, annotatedTx)
76         annotateTxsAccount(annotatedTxs, w.DB)
77
78         rawTx, err := json.Marshal(annotatedTxs[0])
79         if err != nil {
80                 return err
81         }
82
83         w.DB.Set(calcUnconfirmedTxKey(tx.ID.String()), rawTx)
84         return nil
85 }
86
87 // GetUnconfirmedTxByTxID get unconfirmed transaction by txID
88 func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
89         annotatedTx := &query.AnnotatedTx{}
90         txInfo := w.DB.Get(calcUnconfirmedTxKey(txID))
91         if txInfo == nil {
92                 return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
93         }
94
95         if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
96                 return nil, err
97         }
98         annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
99
100         return annotatedTx, nil
101 }
102
103 // SortByTimestamp implements sort.Interface for AnnotatedTx slices
104 type SortByTimestamp []*query.AnnotatedTx
105
106 func (a SortByTimestamp) Len() int           { return len(a) }
107 func (a SortByTimestamp) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
108 func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
109
110 // GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
111 func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
112         annotatedTxs := []*query.AnnotatedTx{}
113
114         txIter := w.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
115         defer txIter.Release()
116         for txIter.Next() {
117                 annotatedTx := &query.AnnotatedTx{}
118                 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
119                         return nil, err
120                 }
121
122                 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
123                         annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
124                         annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
125                 }
126         }
127
128         // sort SortByTimestamp by timestamp
129         sort.Sort(SortByTimestamp(annotatedTxs))
130         return annotatedTxs, nil
131 }