8 log "github.com/sirupsen/logrus"
10 "github.com/bytom/account"
11 "github.com/bytom/blockchain/query"
12 "github.com/bytom/blockchain/signers"
13 "github.com/bytom/consensus"
14 "github.com/bytom/crypto/ed25519/chainkd"
15 chainjson "github.com/bytom/encoding/json"
16 "github.com/bytom/protocol/bc"
17 "github.com/bytom/protocol/bc/types"
20 // POST /list-accounts
21 func (a *API) listAccounts(ctx context.Context, filter struct {
24 accounts, err := a.wallet.AccountMgr.ListAccounts(filter.ID)
26 log.Errorf("listAccounts: %v", err)
27 return NewErrorResponse(err)
30 annotatedAccounts := []query.AnnotatedAccount{}
31 for _, acc := range accounts {
32 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
35 return NewSuccessResponse(annotatedAccounts)
39 func (a *API) getAsset(ctx context.Context, filter struct {
42 asset, err := a.wallet.AssetReg.GetAsset(filter.ID)
44 log.Errorf("getAsset: %v", err)
45 return NewErrorResponse(err)
48 return NewSuccessResponse(asset)
52 func (a *API) listAssets(ctx context.Context, filter struct {
55 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
57 log.Errorf("listAssets: %v", err)
58 return NewErrorResponse(err)
61 return NewSuccessResponse(assets)
64 // POST /list-balances
65 func (a *API) listBalances(ctx context.Context) Response {
66 balances, err := a.wallet.GetAccountBalances("")
68 return NewErrorResponse(err)
70 return NewSuccessResponse(balances)
73 // POST /get-transaction
74 func (a *API) getTransaction(ctx context.Context, txInfo struct {
75 TxID string `json:"tx_id"`
77 var annotatedTx *query.AnnotatedTx
80 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
82 // transaction not found in blockchain db, search it from unconfirmed db
83 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
85 return NewErrorResponse(err)
89 return NewSuccessResponse(annotatedTx)
92 // POST /list-transactions
93 func (a *API) listTransactions(ctx context.Context, filter struct {
95 AccountID string `json:"account_id"`
96 Detail bool `json:"detail"`
97 Unconfirmed bool `json:"unconfirmed"`
99 transactions := []*query.AnnotatedTx{}
101 var transaction *query.AnnotatedTx
104 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
105 if err != nil && filter.Unconfirmed {
106 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
108 return NewErrorResponse(err)
111 transactions = []*query.AnnotatedTx{transaction}
113 transactions, err = a.wallet.GetTransactions(filter.AccountID)
115 return NewErrorResponse(err)
118 if filter.Unconfirmed {
119 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
121 return NewErrorResponse(err)
123 transactions = append(unconfirmedTxs, transactions...)
127 if filter.Detail == false {
128 txSummary := a.wallet.GetTransactionsSummary(transactions)
129 return NewSuccessResponse(txSummary)
131 return NewSuccessResponse(transactions)
134 // POST /get-unconfirmed-transaction
135 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
136 TxID chainjson.HexBytes `json:"tx_id"`
139 copy(tmpTxID[:], filter.TxID[:])
141 txHash := bc.NewHash(tmpTxID)
142 txPool := a.chain.GetTxPool()
143 txDesc, err := txPool.GetTransaction(&txHash)
145 return NewErrorResponse(err)
150 Version: txDesc.Tx.Version,
151 Size: txDesc.Tx.SerializedSize,
152 TimeRange: txDesc.Tx.TimeRange,
153 Inputs: []*query.AnnotatedInput{},
154 Outputs: []*query.AnnotatedOutput{},
158 for i := range txDesc.Tx.Inputs {
159 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
161 for i := range txDesc.Tx.Outputs {
162 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
165 return NewSuccessResponse(tx)
168 type unconfirmedTxsResp struct {
169 Total uint64 `json:"total"`
170 TxIDs []bc.Hash `json:"tx_ids"`
173 // POST /list-unconfirmed-transactions
174 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
177 txPool := a.chain.GetTxPool()
178 txs := txPool.GetTransactions()
179 for _, txDesc := range txs {
180 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
183 return NewSuccessResponse(&unconfirmedTxsResp{
184 Total: uint64(len(txIDs)),
189 // RawTx is the tx struct for getRawTransaction
191 Version uint64 `json:"version"`
192 Size uint64 `json:"size"`
193 TimeRange uint64 `json:"time_range"`
194 Inputs []*query.AnnotatedInput `json:"inputs"`
195 Outputs []*query.AnnotatedOutput `json:"outputs"`
196 Fee int64 `json:"fee"`
199 // POST /decode-raw-transaction
200 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
201 Tx types.Tx `json:"raw_transaction"`
204 Version: ins.Tx.Version,
205 Size: ins.Tx.SerializedSize,
206 TimeRange: ins.Tx.TimeRange,
207 Inputs: []*query.AnnotatedInput{},
208 Outputs: []*query.AnnotatedOutput{},
211 for i := range ins.Tx.Inputs {
212 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
214 for i := range ins.Tx.Outputs {
215 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
218 totalInputBtm := uint64(0)
219 totalOutputBtm := uint64(0)
220 for _, input := range tx.Inputs {
221 if input.AssetID.String() == consensus.BTMAssetID.String() {
222 totalInputBtm += input.Amount
226 for _, output := range tx.Outputs {
227 if output.AssetID.String() == consensus.BTMAssetID.String() {
228 totalOutputBtm += output.Amount
232 tx.Fee = int64(totalInputBtm) - int64(totalOutputBtm)
233 return NewSuccessResponse(tx)
236 // POST /list-unspent-outputs
237 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
238 ID string `json:"id"`
239 SmartContract bool `json:"smart_contract"`
241 accountUTXOs := a.wallet.GetAccountUTXOs(filter.ID, filter.SmartContract)
243 UTXOs := []query.AnnotatedUTXO{}
244 for _, utxo := range accountUTXOs {
245 UTXOs = append([]query.AnnotatedUTXO{{
246 AccountID: utxo.AccountID,
247 OutputID: utxo.OutputID.String(),
248 SourceID: utxo.SourceID.String(),
249 AssetID: utxo.AssetID.String(),
251 SourcePos: utxo.SourcePos,
252 Program: fmt.Sprintf("%x", utxo.ControlProgram),
253 ControlProgramIndex: utxo.ControlProgramIndex,
254 Address: utxo.Address,
255 ValidHeight: utxo.ValidHeight,
256 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
257 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
262 return NewSuccessResponse(UTXOs)
266 func (a *API) gasRate() Response {
267 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
268 return NewSuccessResponse(gasrate)
271 // PubKeyInfo is structure of pubkey info
272 type PubKeyInfo struct {
273 Pubkey string `json:"pubkey"`
274 Path []chainjson.HexBytes `json:"derivation_path"`
277 // AccountPubkey is detail of account pubkey info
278 type AccountPubkey struct {
279 RootXPub chainkd.XPub `json:"root_xpub"`
280 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
283 // POST /list-pubkeys
284 func (a *API) listPubKeys(ctx context.Context, ins struct {
285 AccountID string `json:"account_id"`
287 account, err := a.wallet.AccountMgr.FindByID(ctx, ins.AccountID)
289 return NewErrorResponse(err)
292 pubKeyInfos := []PubKeyInfo{}
293 idx := a.wallet.AccountMgr.GetContractIndex(ins.AccountID)
294 for i := uint64(1); i <= idx; i++ {
295 rawPath := signers.Path(account.Signer, signers.AccountKeySpace, i)
296 derivedXPub := account.XPubs[0].Derive(rawPath)
297 pubkey := derivedXPub.PublicKey()
299 var path []chainjson.HexBytes
300 for _, p := range rawPath {
301 path = append(path, chainjson.HexBytes(p))
304 pubKeyInfos = append([]PubKeyInfo{{
305 Pubkey: hex.EncodeToString(pubkey),
310 return NewSuccessResponse(&AccountPubkey{
311 RootXPub: account.XPubs[0],
312 PubKeyInfos: pubKeyInfos,