9 log "github.com/sirupsen/logrus"
11 "github.com/bytom/bytom/account"
12 "github.com/bytom/bytom/asset"
13 "github.com/bytom/bytom/blockchain/query"
14 "github.com/bytom/bytom/blockchain/signers"
15 "github.com/bytom/bytom/consensus"
16 "github.com/bytom/bytom/crypto/ed25519/chainkd"
17 chainjson "github.com/bytom/bytom/encoding/json"
18 "github.com/bytom/bytom/errors"
19 "github.com/bytom/bytom/protocol/bc"
20 "github.com/bytom/bytom/protocol/bc/types"
23 // POST /list-accounts
24 func (a *API) listAccounts(ctx context.Context, filter struct {
26 Alias string `json:"alias"`
28 accountID := filter.ID
29 if filter.Alias != "" {
30 acc, err := a.wallet.AccountMgr.FindByAlias(filter.Alias)
32 return NewErrorResponse(err)
37 accounts, err := a.wallet.AccountMgr.ListAccounts(accountID)
39 log.Errorf("listAccounts: %v", err)
40 return NewErrorResponse(err)
43 annotatedAccounts := []query.AnnotatedAccount{}
44 for _, acc := range accounts {
45 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
48 return NewSuccessResponse(annotatedAccounts)
52 func (a *API) getAsset(ctx context.Context, filter struct {
55 ass, err := a.wallet.AssetReg.GetAsset(filter.ID)
57 log.Errorf("getAsset: %v", err)
58 return NewErrorResponse(err)
61 annotatedAsset, err := asset.Annotated(ass)
63 return NewErrorResponse(err)
65 return NewSuccessResponse(annotatedAsset)
69 func (a *API) listAssets(ctx context.Context, filter struct {
72 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
74 log.Errorf("listAssets: %v", err)
75 return NewErrorResponse(err)
78 annotatedAssets := []*query.AnnotatedAsset{}
79 for _, ass := range assets {
80 annotatedAsset, err := asset.Annotated(ass)
82 return NewErrorResponse(err)
84 annotatedAssets = append(annotatedAssets, annotatedAsset)
86 return NewSuccessResponse(annotatedAssets)
89 // POST /list-balances
90 func (a *API) listBalances(ctx context.Context, filter struct {
91 AccountID string `json:"account_id"`
92 AccountAlias string `json:"account_alias"`
94 accountID := filter.AccountID
95 if filter.AccountAlias != "" {
96 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
98 return NewErrorResponse(err)
103 balances, err := a.wallet.GetAccountBalances(accountID, "")
105 return NewErrorResponse(err)
107 return NewSuccessResponse(balances)
110 // POST /get-transaction
111 func (a *API) getTransaction(ctx context.Context, txInfo struct {
112 TxID string `json:"tx_id"`
114 var annotatedTx *query.AnnotatedTx
117 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
119 // transaction not found in blockchain db, search it from unconfirmed db
120 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
122 return NewErrorResponse(err)
126 return NewSuccessResponse(annotatedTx)
129 // POST /list-transactions
130 func (a *API) listTransactions(ctx context.Context, filter struct {
131 ID string `json:"id"`
132 AccountID string `json:"account_id"`
133 Detail bool `json:"detail"`
134 Unconfirmed bool `json:"unconfirmed"`
135 From uint `json:"from"`
136 Count uint `json:"count"`
138 transactions := []*query.AnnotatedTx{}
140 var transaction *query.AnnotatedTx
143 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
144 if err != nil && filter.Unconfirmed {
145 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
149 return NewErrorResponse(err)
151 transactions = []*query.AnnotatedTx{transaction}
153 transactions, err = a.wallet.GetTransactions(filter.AccountID)
155 return NewErrorResponse(err)
158 if filter.Unconfirmed {
159 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
161 return NewErrorResponse(err)
163 transactions = append(unconfirmedTxs, transactions...)
167 if filter.Detail == false {
168 txSummary := a.wallet.GetTransactionsSummary(transactions)
169 start, end := getPageRange(len(txSummary), filter.From, filter.Count)
170 return NewSuccessResponse(txSummary[start:end])
172 start, end := getPageRange(len(transactions), filter.From, filter.Count)
173 return NewSuccessResponse(transactions[start:end])
176 // POST /get-unconfirmed-transaction
177 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
178 TxID chainjson.HexBytes `json:"tx_id"`
181 copy(tmpTxID[:], filter.TxID[:])
183 txHash := bc.NewHash(tmpTxID)
184 txPool := a.chain.GetTxPool()
185 txDesc, err := txPool.GetTransaction(&txHash)
187 return NewErrorResponse(err)
192 Version: txDesc.Tx.Version,
193 Size: txDesc.Tx.SerializedSize,
194 TimeRange: txDesc.Tx.TimeRange,
195 Inputs: []*query.AnnotatedInput{},
196 Outputs: []*query.AnnotatedOutput{},
199 resOutID := txDesc.Tx.ResultIds[0]
200 resOut := txDesc.Tx.Entries[*resOutID]
201 switch out := resOut.(type) {
202 case *bc.OriginalOutput:
203 tx.MuxID = *out.Source.Ref
205 tx.MuxID = *out.Source.Ref
208 for i := range txDesc.Tx.Inputs {
209 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
211 for i := range txDesc.Tx.Outputs {
212 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
215 return NewSuccessResponse(tx)
218 type unconfirmedTxsResp struct {
219 Total uint64 `json:"total"`
220 TxIDs []bc.Hash `json:"tx_ids"`
223 // POST /list-unconfirmed-transactions
224 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
227 txPool := a.chain.GetTxPool()
228 txs := txPool.GetTransactions()
229 for _, txDesc := range txs {
230 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
233 return NewSuccessResponse(&unconfirmedTxsResp{
234 Total: uint64(len(txIDs)),
239 // RawTx is the tx struct for getRawTransaction
241 ID bc.Hash `json:"tx_id"`
242 Version uint64 `json:"version"`
243 Size uint64 `json:"size"`
244 TimeRange uint64 `json:"time_range"`
245 Inputs []*query.AnnotatedInput `json:"inputs"`
246 Outputs []*query.AnnotatedOutput `json:"outputs"`
247 Fee uint64 `json:"fee"`
250 // POST /decode-raw-transaction
251 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
252 Tx types.Tx `json:"raw_transaction"`
256 Version: ins.Tx.Version,
257 Size: ins.Tx.SerializedSize,
258 TimeRange: ins.Tx.TimeRange,
259 Inputs: []*query.AnnotatedInput{},
260 Outputs: []*query.AnnotatedOutput{},
263 for i := range ins.Tx.Inputs {
264 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
266 for i := range ins.Tx.Outputs {
267 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
270 tx.Fee = ins.Tx.Fee()
271 return NewSuccessResponse(tx)
274 // POST /list-unspent-outputs
275 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
276 AccountID string `json:"account_id"`
277 AccountAlias string `json:"account_alias"`
278 ID string `json:"id"`
279 Unconfirmed bool `json:"unconfirmed"`
280 SmartContract bool `json:"smart_contract"`
281 From uint `json:"from"`
282 Count uint `json:"count"`
284 accountID := filter.AccountID
285 if filter.AccountAlias != "" {
286 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
288 return NewErrorResponse(err)
292 accountUTXOs := a.wallet.GetAccountUtxos(accountID, filter.ID, filter.Unconfirmed, filter.SmartContract, false)
294 UTXOs := []query.AnnotatedUTXO{}
295 for _, utxo := range accountUTXOs {
296 UTXOs = append([]query.AnnotatedUTXO{{
297 AccountID: utxo.AccountID,
298 OutputID: utxo.OutputID.String(),
299 SourceID: utxo.SourceID.String(),
300 AssetID: utxo.AssetID.String(),
302 SourcePos: utxo.SourcePos,
303 Program: fmt.Sprintf("%x", utxo.ControlProgram),
304 ControlProgramIndex: utxo.ControlProgramIndex,
305 Address: utxo.Address,
306 ValidHeight: utxo.ValidHeight,
307 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
308 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
312 start, end := getPageRange(len(UTXOs), filter.From, filter.Count)
313 return NewSuccessResponse(UTXOs[start:end])
317 func (a *API) gasRate() Response {
318 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
319 return NewSuccessResponse(gasrate)
322 // PubKeyInfo is structure of pubkey info
323 type PubKeyInfo struct {
324 Pubkey string `json:"pubkey"`
325 Path []chainjson.HexBytes `json:"derivation_path"`
328 // AccountPubkey is detail of account pubkey info
329 type AccountPubkey struct {
330 RootXPub chainkd.XPub `json:"root_xpub"`
331 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
334 func getPubkey(account *account.Account, change bool, index uint64) (*ed25519.PublicKey, []chainjson.HexBytes, error) {
335 rawPath, err := signers.Path(account.Signer, signers.AccountKeySpace, change, index)
339 derivedXPub := account.XPubs[0].Derive(rawPath)
340 pubkey := derivedXPub.PublicKey()
341 var path []chainjson.HexBytes
342 for _, p := range rawPath {
343 path = append(path, chainjson.HexBytes(p))
346 return &pubkey, path, nil
349 // POST /list-pubkeys
350 func (a *API) listPubKeys(ctx context.Context, ins struct {
351 AccountID string `json:"account_id"`
352 AccountAlias string `json:"account_alias"`
353 PublicKey string `json:"public_key"`
356 account := &account.Account{}
357 if ins.AccountAlias != "" {
358 account, err = a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
360 account, err = a.wallet.AccountMgr.FindByID(ins.AccountID)
364 return NewErrorResponse(err)
367 pubKeyInfos := []PubKeyInfo{}
368 if account.DeriveRule == signers.BIP0032 {
369 idx := a.wallet.AccountMgr.GetContractIndex(account.ID)
370 for i := uint64(1); i <= idx; i++ {
371 pubkey, path, err := getPubkey(account, false, i)
373 return NewErrorResponse(err)
375 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
378 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
379 Pubkey: hex.EncodeToString(*pubkey),
383 } else if account.DeriveRule == signers.BIP0044 {
384 idx := a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, true)
385 for i := uint64(1); i <= idx; i++ {
386 pubkey, path, err := getPubkey(account, true, i)
388 return NewErrorResponse(err)
390 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
393 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
394 Pubkey: hex.EncodeToString(*pubkey),
399 idx = a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, false)
400 for i := uint64(1); i <= idx; i++ {
401 pubkey, path, err := getPubkey(account, false, i)
403 return NewErrorResponse(err)
405 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
408 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
409 Pubkey: hex.EncodeToString(*pubkey),
415 if len(pubKeyInfos) == 0 {
416 return NewErrorResponse(errors.New("Not found publickey for the account"))
419 return NewSuccessResponse(&AccountPubkey{
420 RootXPub: account.XPubs[0],
421 PubKeyInfos: pubKeyInfos,
425 func (a *API) listAccountVotes(ctx context.Context, filter struct {
426 AccountID string `json:"account_id"`
427 AccountAlias string `json:"account_alias"`
429 accountID := filter.AccountID
430 if filter.AccountAlias != "" {
431 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
433 return NewErrorResponse(err)
438 votes, err := a.wallet.GetAccountVotes(accountID, "")
440 return NewErrorResponse(err)
442 return NewSuccessResponse(votes)