OSDN Git Service

Merge pull request #1042 from Bytom/dev
[bytom/bytom.git] / api / query.go
1 package api
2
3 import (
4         "context"
5         "fmt"
6
7         log "github.com/sirupsen/logrus"
8
9         "github.com/bytom/account"
10         "github.com/bytom/blockchain/query"
11         "github.com/bytom/consensus"
12         chainjson "github.com/bytom/encoding/json"
13         "github.com/bytom/protocol/bc"
14         "github.com/bytom/protocol/bc/types"
15 )
16
17 // POST /list-accounts
18 func (a *API) listAccounts(ctx context.Context, filter struct {
19         ID string `json:"id"`
20 }) Response {
21         accounts, err := a.wallet.AccountMgr.ListAccounts(filter.ID)
22         if err != nil {
23                 log.Errorf("listAccounts: %v", err)
24                 return NewErrorResponse(err)
25         }
26
27         annotatedAccounts := []query.AnnotatedAccount{}
28         for _, acc := range accounts {
29                 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
30         }
31
32         return NewSuccessResponse(annotatedAccounts)
33 }
34
35 // POST /get-asset
36 func (a *API) getAsset(ctx context.Context, filter struct {
37         ID string `json:"id"`
38 }) Response {
39         asset, err := a.wallet.AssetReg.GetAsset(filter.ID)
40         if err != nil {
41                 log.Errorf("getAsset: %v", err)
42                 return NewErrorResponse(err)
43         }
44
45         return NewSuccessResponse(asset)
46 }
47
48 // POST /list-assets
49 func (a *API) listAssets(ctx context.Context, filter struct {
50         ID string `json:"id"`
51 }) Response {
52         assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
53         if err != nil {
54                 log.Errorf("listAssets: %v", err)
55                 return NewErrorResponse(err)
56         }
57
58         return NewSuccessResponse(assets)
59 }
60
61 // POST /list-balances
62 func (a *API) listBalances(ctx context.Context) Response {
63         balances, err := a.wallet.GetAccountBalances("")
64         if err != nil {
65                 return NewErrorResponse(err)
66         }
67         return NewSuccessResponse(balances)
68 }
69
70 // POST /get-transaction
71 func (a *API) getTransaction(ctx context.Context, txInfo struct {
72         TxID string `json:"tx_id"`
73 }) Response {
74         var annotatedTx *query.AnnotatedTx
75         var err error
76
77         annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
78         if err != nil {
79                 // transaction not found in blockchain db, search it from unconfirmed db
80                 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
81                 if err != nil {
82                         return NewErrorResponse(err)
83                 }
84         }
85
86         return NewSuccessResponse(annotatedTx)
87 }
88
89 // POST /list-transactions
90 func (a *API) listTransactions(ctx context.Context, filter struct {
91         ID          string `json:"id"`
92         AccountID   string `json:"account_id"`
93         Detail      bool   `json:"detail"`
94         Unconfirmed bool   `json:"unconfirmed"`
95 }) Response {
96         transactions := []*query.AnnotatedTx{}
97         var err error
98         var transaction *query.AnnotatedTx
99
100         if filter.ID != "" {
101                 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
102                 if err != nil && filter.Unconfirmed {
103                         transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
104                         if err != nil {
105                                 return NewErrorResponse(err)
106                         }
107                 }
108                 transactions = []*query.AnnotatedTx{transaction}
109         } else {
110                 transactions, err = a.wallet.GetTransactions(filter.AccountID)
111                 if err != nil {
112                         return NewErrorResponse(err)
113                 }
114
115                 if filter.Unconfirmed {
116                         unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
117                         if err != nil {
118                                 return NewErrorResponse(err)
119                         }
120                         transactions = append(unconfirmedTxs, transactions...)
121                 }
122         }
123
124         if filter.Detail == false {
125                 txSummary := a.wallet.GetTransactionsSummary(transactions)
126                 return NewSuccessResponse(txSummary)
127         }
128         return NewSuccessResponse(transactions)
129 }
130
131 // POST /get-unconfirmed-transaction
132 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
133         TxID chainjson.HexBytes `json:"tx_id"`
134 }) Response {
135         var tmpTxID [32]byte
136         copy(tmpTxID[:], filter.TxID[:])
137
138         txHash := bc.NewHash(tmpTxID)
139         txPool := a.chain.GetTxPool()
140         txDesc, err := txPool.GetTransaction(&txHash)
141         if err != nil {
142                 return NewErrorResponse(err)
143         }
144
145         tx := &BlockTx{
146                 ID:         txDesc.Tx.ID,
147                 Version:    txDesc.Tx.Version,
148                 Size:       txDesc.Tx.SerializedSize,
149                 TimeRange:  txDesc.Tx.TimeRange,
150                 Inputs:     []*query.AnnotatedInput{},
151                 Outputs:    []*query.AnnotatedOutput{},
152                 StatusFail: false,
153         }
154
155         for i := range txDesc.Tx.Inputs {
156                 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
157         }
158         for i := range txDesc.Tx.Outputs {
159                 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
160         }
161
162         return NewSuccessResponse(tx)
163 }
164
165 type unconfirmedTxsResp struct {
166         Total uint64    `json:"total"`
167         TxIDs []bc.Hash `json:"tx_ids"`
168 }
169
170 // POST /list-unconfirmed-transactions
171 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
172         txIDs := []bc.Hash{}
173
174         txPool := a.chain.GetTxPool()
175         txs := txPool.GetTransactions()
176         for _, txDesc := range txs {
177                 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
178         }
179
180         return NewSuccessResponse(&unconfirmedTxsResp{
181                 Total: uint64(len(txIDs)),
182                 TxIDs: txIDs,
183         })
184 }
185
186 // RawTx is the tx struct for getRawTransaction
187 type RawTx struct {
188         Version   uint64                   `json:"version"`
189         Size      uint64                   `json:"size"`
190         TimeRange uint64                   `json:"time_range"`
191         Inputs    []*query.AnnotatedInput  `json:"inputs"`
192         Outputs   []*query.AnnotatedOutput `json:"outputs"`
193         Fee       int64                    `json:"fee"`
194 }
195
196 // POST /decode-raw-transaction
197 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
198         Tx types.Tx `json:"raw_transaction"`
199 }) Response {
200         tx := &RawTx{
201                 Version:   ins.Tx.Version,
202                 Size:      ins.Tx.SerializedSize,
203                 TimeRange: ins.Tx.TimeRange,
204                 Inputs:    []*query.AnnotatedInput{},
205                 Outputs:   []*query.AnnotatedOutput{},
206         }
207
208         for i := range ins.Tx.Inputs {
209                 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
210         }
211         for i := range ins.Tx.Outputs {
212                 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
213         }
214
215         totalInputBtm := uint64(0)
216         totalOutputBtm := uint64(0)
217         for _, input := range tx.Inputs {
218                 if input.AssetID.String() == consensus.BTMAssetID.String() {
219                         totalInputBtm += input.Amount
220                 }
221         }
222
223         for _, output := range tx.Outputs {
224                 if output.AssetID.String() == consensus.BTMAssetID.String() {
225                         totalOutputBtm += output.Amount
226                 }
227         }
228
229         tx.Fee = int64(totalInputBtm) - int64(totalOutputBtm)
230         return NewSuccessResponse(tx)
231 }
232
233 // POST /list-unspent-outputs
234 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
235         ID string `json:"id"`
236 }) Response {
237         accountUTXOs := a.wallet.GetAccountUTXOs(filter.ID)
238
239         UTXOs := []query.AnnotatedUTXO{}
240         for _, utxo := range accountUTXOs {
241                 UTXOs = append([]query.AnnotatedUTXO{{
242                         AccountID:           utxo.AccountID,
243                         OutputID:            utxo.OutputID.String(),
244                         SourceID:            utxo.SourceID.String(),
245                         AssetID:             utxo.AssetID.String(),
246                         Amount:              utxo.Amount,
247                         SourcePos:           utxo.SourcePos,
248                         Program:             fmt.Sprintf("%x", utxo.ControlProgram),
249                         ControlProgramIndex: utxo.ControlProgramIndex,
250                         Address:             utxo.Address,
251                         ValidHeight:         utxo.ValidHeight,
252                         Alias:               a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
253                         AssetAlias:          a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
254                         Change:              utxo.Change,
255                 }}, UTXOs...)
256         }
257
258         return NewSuccessResponse(UTXOs)
259 }
260
261 // return gasRate
262 func (a *API) gasRate() Response {
263         gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
264         return NewSuccessResponse(gasrate)
265 }