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/errors"
17 "github.com/bytom/protocol/bc"
18 "github.com/bytom/protocol/bc/types"
21 // POST /list-accounts
22 func (a *API) listAccounts(ctx context.Context, filter struct {
24 Alias string `json:"alias"`
26 accountID := filter.ID
27 if filter.Alias != "" {
28 acc, err := a.wallet.AccountMgr.FindByAlias(filter.Alias)
30 return NewErrorResponse(err)
35 accounts, err := a.wallet.AccountMgr.ListAccounts(accountID)
37 log.Errorf("listAccounts: %v", err)
38 return NewErrorResponse(err)
41 annotatedAccounts := []query.AnnotatedAccount{}
42 for _, acc := range accounts {
43 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
46 return NewSuccessResponse(annotatedAccounts)
50 func (a *API) getAsset(ctx context.Context, filter struct {
53 asset, err := a.wallet.AssetReg.GetAsset(filter.ID)
55 log.Errorf("getAsset: %v", err)
56 return NewErrorResponse(err)
59 return NewSuccessResponse(asset)
63 func (a *API) listAssets(ctx context.Context, filter struct {
66 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
68 log.Errorf("listAssets: %v", err)
69 return NewErrorResponse(err)
72 return NewSuccessResponse(assets)
75 // POST /list-balances
76 func (a *API) listBalances(ctx context.Context) Response {
77 balances, err := a.wallet.GetAccountBalances("")
79 return NewErrorResponse(err)
81 return NewSuccessResponse(balances)
84 // POST /get-transaction
85 func (a *API) getTransaction(ctx context.Context, txInfo struct {
86 TxID string `json:"tx_id"`
88 var annotatedTx *query.AnnotatedTx
91 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
93 // transaction not found in blockchain db, search it from unconfirmed db
94 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
96 return NewErrorResponse(err)
100 return NewSuccessResponse(annotatedTx)
103 // POST /list-transactions
104 func (a *API) listTransactions(ctx context.Context, filter struct {
105 ID string `json:"id"`
106 AccountID string `json:"account_id"`
107 Detail bool `json:"detail"`
108 Unconfirmed bool `json:"unconfirmed"`
109 From uint `json:"from"`
110 Count uint `json:"count"`
112 transactions := []*query.AnnotatedTx{}
114 var transaction *query.AnnotatedTx
117 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
118 if err != nil && filter.Unconfirmed {
119 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
121 return NewErrorResponse(err)
124 transactions = []*query.AnnotatedTx{transaction}
126 transactions, err = a.wallet.GetTransactions(filter.AccountID)
128 return NewErrorResponse(err)
131 if filter.Unconfirmed {
132 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
134 return NewErrorResponse(err)
136 transactions = append(unconfirmedTxs, transactions...)
140 if filter.Detail == false {
141 txSummary := a.wallet.GetTransactionsSummary(transactions)
142 start, end := getPageRange(len(txSummary), filter.From, filter.Count)
143 return NewSuccessResponse(txSummary[start:end])
145 start, end := getPageRange(len(transactions), filter.From, filter.Count)
146 return NewSuccessResponse(transactions[start:end])
149 // POST /get-unconfirmed-transaction
150 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
151 TxID chainjson.HexBytes `json:"tx_id"`
154 copy(tmpTxID[:], filter.TxID[:])
156 txHash := bc.NewHash(tmpTxID)
157 txPool := a.chain.GetTxPool()
158 txDesc, err := txPool.GetTransaction(&txHash)
160 return NewErrorResponse(err)
165 Version: txDesc.Tx.Version,
166 Size: txDesc.Tx.SerializedSize,
167 TimeRange: txDesc.Tx.TimeRange,
168 Inputs: []*query.AnnotatedInput{},
169 Outputs: []*query.AnnotatedOutput{},
173 for i := range txDesc.Tx.Inputs {
174 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
176 for i := range txDesc.Tx.Outputs {
177 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
180 return NewSuccessResponse(tx)
183 type unconfirmedTxsResp struct {
184 Total uint64 `json:"total"`
185 TxIDs []bc.Hash `json:"tx_ids"`
188 // POST /list-unconfirmed-transactions
189 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
192 txPool := a.chain.GetTxPool()
193 txs := txPool.GetTransactions()
194 for _, txDesc := range txs {
195 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
198 return NewSuccessResponse(&unconfirmedTxsResp{
199 Total: uint64(len(txIDs)),
204 // RawTx is the tx struct for getRawTransaction
206 ID bc.Hash `json:"tx_id"`
207 Version uint64 `json:"version"`
208 Size uint64 `json:"size"`
209 TimeRange uint64 `json:"time_range"`
210 Inputs []*query.AnnotatedInput `json:"inputs"`
211 Outputs []*query.AnnotatedOutput `json:"outputs"`
212 Fee int64 `json:"fee"`
215 // POST /decode-raw-transaction
216 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
217 Tx types.Tx `json:"raw_transaction"`
221 Version: ins.Tx.Version,
222 Size: ins.Tx.SerializedSize,
223 TimeRange: ins.Tx.TimeRange,
224 Inputs: []*query.AnnotatedInput{},
225 Outputs: []*query.AnnotatedOutput{},
228 for i := range ins.Tx.Inputs {
229 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
231 for i := range ins.Tx.Outputs {
232 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
235 totalInputBtm := uint64(0)
236 totalOutputBtm := uint64(0)
237 for _, input := range tx.Inputs {
238 if input.AssetID.String() == consensus.BTMAssetID.String() {
239 totalInputBtm += input.Amount
243 for _, output := range tx.Outputs {
244 if output.AssetID.String() == consensus.BTMAssetID.String() {
245 totalOutputBtm += output.Amount
249 tx.Fee = int64(totalInputBtm) - int64(totalOutputBtm)
250 return NewSuccessResponse(tx)
253 // POST /list-unspent-outputs
254 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
255 ID string `json:"id"`
256 Unconfirmed bool `json:"unconfirmed"`
257 SmartContract bool `json:"smart_contract"`
258 From uint `json:"from"`
259 Count uint `json:"count"`
261 accountUTXOs := a.wallet.GetAccountUtxos(filter.ID, filter.Unconfirmed, filter.SmartContract)
263 UTXOs := []query.AnnotatedUTXO{}
264 for _, utxo := range accountUTXOs {
265 UTXOs = append([]query.AnnotatedUTXO{{
266 AccountID: utxo.AccountID,
267 OutputID: utxo.OutputID.String(),
268 SourceID: utxo.SourceID.String(),
269 AssetID: utxo.AssetID.String(),
271 SourcePos: utxo.SourcePos,
272 Program: fmt.Sprintf("%x", utxo.ControlProgram),
273 ControlProgramIndex: utxo.ControlProgramIndex,
274 Address: utxo.Address,
275 ValidHeight: utxo.ValidHeight,
276 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
277 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
281 start, end := getPageRange(len(UTXOs), filter.From, filter.Count)
282 return NewSuccessResponse(UTXOs[start:end])
286 func (a *API) gasRate() Response {
287 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
288 return NewSuccessResponse(gasrate)
291 // PubKeyInfo is structure of pubkey info
292 type PubKeyInfo struct {
293 Pubkey string `json:"pubkey"`
294 Path []chainjson.HexBytes `json:"derivation_path"`
297 // AccountPubkey is detail of account pubkey info
298 type AccountPubkey struct {
299 RootXPub chainkd.XPub `json:"root_xpub"`
300 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
303 // POST /list-pubkeys
304 func (a *API) listPubKeys(ctx context.Context, ins struct {
305 AccountID string `json:"account_id"`
306 AccountAlias string `json:"account_alias"`
307 PublicKey string `json:"public_key"`
310 account := &account.Account{}
311 if ins.AccountAlias != "" {
312 account, err = a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
314 account, err = a.wallet.AccountMgr.FindByID(ins.AccountID)
318 return NewErrorResponse(err)
321 pubKeyInfos := []PubKeyInfo{}
322 idx := a.wallet.AccountMgr.GetContractIndex(account.ID)
323 for i := uint64(1); i <= idx; i++ {
324 rawPath := signers.Path(account.Signer, signers.AccountKeySpace, i)
325 derivedXPub := account.XPubs[0].Derive(rawPath)
326 pubkey := derivedXPub.PublicKey()
328 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(pubkey) {
332 var path []chainjson.HexBytes
333 for _, p := range rawPath {
334 path = append(path, chainjson.HexBytes(p))
337 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
338 Pubkey: hex.EncodeToString(pubkey),
343 if len(pubKeyInfos) == 0 {
344 return NewErrorResponse(errors.New("Not found publickey for the account"))
347 return NewSuccessResponse(&AccountPubkey{
348 RootXPub: account.XPubs[0],
349 PubKeyInfos: pubKeyInfos,