9 log "github.com/sirupsen/logrus"
11 "github.com/bytom/account"
12 "github.com/bytom/blockchain/query"
13 "github.com/bytom/crypto/sha3pool"
14 "github.com/bytom/protocol"
15 "github.com/bytom/protocol/bc/types"
19 //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
20 UnconfirmedTxPrefix = "UTXS:"
23 func calcUnconfirmedTxKey(formatKey string) []byte {
24 return []byte(UnconfirmedTxPrefix + formatKey)
27 // SortByTimestamp implements sort.Interface for AnnotatedTx slices
28 type SortByTimestamp []*query.AnnotatedTx
30 func (a SortByTimestamp) Len() int { return len(a) }
31 func (a SortByTimestamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
32 func (a SortByTimestamp) Less(i, j int) bool { return a[i].Timestamp > a[j].Timestamp }
34 // AddUnconfirmedTx handle wallet status update when tx add into txpool
35 func (w *Wallet) AddUnconfirmedTx(txD *protocol.TxDesc) {
36 if err := w.saveUnconfirmedTx(txD.Tx); err != nil {
37 log.WithField("err", err).Error("wallet fail on saveUnconfirmedTx")
40 utxos := txOutToUtxos(txD.Tx, txD.StatusFail, 0)
41 utxos = w.filterAccountUtxo(utxos)
42 w.AccountMgr.AddUnconfirmedUtxo(utxos)
45 // GetUnconfirmedTxs get account unconfirmed transactions, filter transactions by accountID when accountID is not empty
46 func (w *Wallet) GetUnconfirmedTxs(accountID string) ([]*query.AnnotatedTx, error) {
47 annotatedTxs := []*query.AnnotatedTx{}
48 txIter := w.DB.IteratorPrefix([]byte(UnconfirmedTxPrefix))
49 defer txIter.Release()
52 annotatedTx := &query.AnnotatedTx{}
53 if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {
57 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
58 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
59 annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)
63 sort.Sort(SortByTimestamp(annotatedTxs))
64 return annotatedTxs, nil
67 // GetUnconfirmedTxByTxID get unconfirmed transaction by txID
68 func (w *Wallet) GetUnconfirmedTxByTxID(txID string) (*query.AnnotatedTx, error) {
69 annotatedTx := &query.AnnotatedTx{}
70 txInfo := w.DB.Get(calcUnconfirmedTxKey(txID))
72 return nil, fmt.Errorf("No transaction(tx_id=%s) from txpool", txID)
75 if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
79 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
80 return annotatedTx, nil
83 // RemoveUnconfirmedTx handle wallet status update when tx removed from txpool
84 func (w *Wallet) RemoveUnconfirmedTx(txD *protocol.TxDesc) {
85 w.AccountMgr.RemoveUnconfirmedUtxo(txD.Tx.ResultIds)
88 func (w *Wallet) buildAnnotatedUnconfirmedTx(tx *types.Tx) *query.AnnotatedTx {
89 annotatedTx := &query.AnnotatedTx{
91 Timestamp: uint64(time.Now().Unix()),
92 Inputs: make([]*query.AnnotatedInput, 0, len(tx.Inputs)),
93 Outputs: make([]*query.AnnotatedOutput, 0, len(tx.Outputs)),
94 Size: tx.SerializedSize,
97 for i := range tx.Inputs {
98 annotatedTx.Inputs = append(annotatedTx.Inputs, w.BuildAnnotatedInput(tx, uint32(i)))
100 for i := range tx.Outputs {
101 annotatedTx.Outputs = append(annotatedTx.Outputs, w.BuildAnnotatedOutput(tx, i))
106 // checkRelatedTransaction check related unconfirmed transaction.
107 func (w *Wallet) checkRelatedTransaction(tx *types.Tx) bool {
108 for _, v := range tx.Outputs {
110 sha3pool.Sum256(hash[:], v.ControlProgram)
111 if bytes := w.DB.Get(account.ContractKey(hash)); bytes != nil {
116 for _, v := range tx.Inputs {
117 outid, err := v.SpentOutputID()
121 if bytes := w.DB.Get(account.StandardUTXOKey(outid)); bytes != nil {
128 // SaveUnconfirmedTx save unconfirmed annotated transaction to the database
129 func (w *Wallet) saveUnconfirmedTx(tx *types.Tx) error {
130 if !w.checkRelatedTransaction(tx) {
134 // annotate account and asset
135 annotatedTx := w.buildAnnotatedUnconfirmedTx(tx)
136 annotatedTxs := []*query.AnnotatedTx{}
137 annotatedTxs = append(annotatedTxs, annotatedTx)
138 annotateTxsAccount(annotatedTxs, w.DB)
140 rawTx, err := json.Marshal(annotatedTxs[0])
145 w.DB.Set(calcUnconfirmedTxKey(tx.ID.String()), rawTx)