8 log "github.com/sirupsen/logrus"
10 "github.com/vapor/account"
11 "github.com/vapor/asset"
12 "github.com/vapor/blockchain/query"
13 "github.com/vapor/blockchain/signers"
14 "github.com/vapor/blockchain/txbuilder"
15 "github.com/vapor/consensus"
16 "github.com/vapor/crypto/ed25519"
17 "github.com/vapor/crypto/ed25519/chainkd"
18 chainjson "github.com/vapor/encoding/json"
19 "github.com/vapor/errors"
20 "github.com/vapor/protocol/bc"
21 "github.com/vapor/protocol/bc/types"
24 // POST /list-accounts
25 func (a *API) listAccounts(ctx context.Context, filter struct {
27 Alias string `json:"alias"`
29 accountID := filter.ID
30 if filter.Alias != "" {
31 acc, err := a.wallet.AccountMgr.FindByAlias(filter.Alias)
33 return NewErrorResponse(err)
38 accounts, err := a.wallet.AccountMgr.ListAccounts(accountID)
40 log.Errorf("listAccounts: %v", err)
41 return NewErrorResponse(err)
44 annotatedAccounts := []query.AnnotatedAccount{}
45 for _, acc := range accounts {
46 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
49 return NewSuccessResponse(annotatedAccounts)
53 func (a *API) getAsset(ctx context.Context, filter struct {
56 ass, err := a.wallet.AssetReg.GetAsset(filter.ID)
58 log.Errorf("getAsset: %v", err)
59 return NewErrorResponse(err)
62 annotatedAsset, err := asset.Annotated(ass)
64 return NewErrorResponse(err)
66 return NewSuccessResponse(annotatedAsset)
70 func (a *API) listAssets(ctx context.Context, filter struct {
73 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
75 log.Errorf("listAssets: %v", err)
76 return NewErrorResponse(err)
79 annotatedAssets := []*query.AnnotatedAsset{}
80 for _, ass := range assets {
81 annotatedAsset, err := asset.Annotated(ass)
83 return NewErrorResponse(err)
85 annotatedAssets = append(annotatedAssets, annotatedAsset)
87 return NewSuccessResponse(annotatedAssets)
90 // POST /list-balances
91 func (a *API) listBalances(ctx context.Context, filter struct {
92 AccountID string `json:"account_id"`
93 AccountAlias string `json:"account_alias"`
95 accountID := filter.AccountID
96 if filter.AccountAlias != "" {
97 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
99 return NewErrorResponse(err)
104 balances, err := a.wallet.GetAccountBalances(accountID, "")
106 return NewErrorResponse(err)
108 return NewSuccessResponse(balances)
111 func (a *API) listAccountVotes(ctx context.Context, filter struct {
112 AccountID string `json:"account_id"`
113 AccountAlias string `json:"account_alias"`
115 accountID := filter.AccountID
116 if filter.AccountAlias != "" {
117 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
119 return NewErrorResponse(err)
124 votes, err := a.wallet.GetAccountVotes(accountID, "")
126 return NewErrorResponse(err)
128 return NewSuccessResponse(votes)
131 // POST /get-transaction
132 func (a *API) getTransaction(ctx context.Context, txInfo struct {
133 TxID string `json:"tx_id"`
135 var annotatedTx *query.AnnotatedTx
138 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
140 // transaction not found in blockchain db, search it from unconfirmed db
141 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
143 return NewErrorResponse(err)
147 return NewSuccessResponse(annotatedTx)
150 // POST /list-transactions
151 func (a *API) listTransactions(ctx context.Context, filter struct {
152 ID string `json:"id"`
153 AccountID string `json:"account_id"`
154 Detail bool `json:"detail"`
155 Unconfirmed bool `json:"unconfirmed"`
156 From uint `json:"from"`
157 Count uint `json:"count"`
159 transactions := []*query.AnnotatedTx{}
161 var transaction *query.AnnotatedTx
164 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
165 if err != nil && filter.Unconfirmed {
166 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
170 return NewErrorResponse(err)
172 transactions = []*query.AnnotatedTx{transaction}
174 transactions, err = a.wallet.GetTransactions(filter.AccountID)
176 return NewErrorResponse(err)
179 if filter.Unconfirmed {
180 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
182 return NewErrorResponse(err)
184 transactions = append(unconfirmedTxs, transactions...)
188 if filter.Detail == false {
189 txSummary := a.wallet.GetTransactionsSummary(transactions)
190 start, end := getPageRange(len(txSummary), filter.From, filter.Count)
191 return NewSuccessResponse(txSummary[start:end])
193 start, end := getPageRange(len(transactions), filter.From, filter.Count)
194 return NewSuccessResponse(transactions[start:end])
197 // POST /get-unconfirmed-transaction
198 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
199 TxID chainjson.HexBytes `json:"tx_id"`
202 copy(tmpTxID[:], filter.TxID[:])
204 txHash := bc.NewHash(tmpTxID)
205 txPool := a.chain.GetTxPool()
206 txDesc, err := txPool.GetTransaction(&txHash)
208 return NewErrorResponse(err)
213 Version: txDesc.Tx.Version,
214 Size: txDesc.Tx.SerializedSize,
215 TimeRange: txDesc.Tx.TimeRange,
216 Inputs: []*query.AnnotatedInput{},
217 Outputs: []*query.AnnotatedOutput{},
218 StatusFail: txDesc.StatusFail,
221 resOutID := txDesc.Tx.ResultIds[0]
222 resOut := txDesc.Tx.Entries[*resOutID]
223 switch out := resOut.(type) {
224 case *bc.IntraChainOutput:
225 tx.MuxID = *out.Source.Ref
227 tx.MuxID = *out.Source.Ref
229 tx.MuxID = *out.Source.Ref
232 for i := range txDesc.Tx.Inputs {
233 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
235 for i := range txDesc.Tx.Outputs {
236 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
239 return NewSuccessResponse(tx)
242 type unconfirmedTxsResp struct {
243 Total uint64 `json:"total"`
244 TxIDs []bc.Hash `json:"tx_ids"`
247 // POST /list-unconfirmed-transactions
248 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
251 txPool := a.chain.GetTxPool()
252 txs := txPool.GetTransactions()
253 for _, txDesc := range txs {
254 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
257 return NewSuccessResponse(&unconfirmedTxsResp{
258 Total: uint64(len(txIDs)),
263 // RawTx is the tx struct for getRawTransaction
265 ID bc.Hash `json:"tx_id"`
266 Version uint64 `json:"version"`
267 Size uint64 `json:"size"`
268 TimeRange uint64 `json:"time_range"`
269 Inputs []*query.AnnotatedInput `json:"inputs"`
270 Outputs []*query.AnnotatedOutput `json:"outputs"`
271 Fee uint64 `json:"fee"`
274 // POST /decode-raw-transaction
275 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
276 Tx types.Tx `json:"raw_transaction"`
280 Version: ins.Tx.Version,
281 Size: ins.Tx.SerializedSize,
282 TimeRange: ins.Tx.TimeRange,
283 Inputs: []*query.AnnotatedInput{},
284 Outputs: []*query.AnnotatedOutput{},
287 for i := range ins.Tx.Inputs {
288 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
290 for i := range ins.Tx.Outputs {
291 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
294 tx.Fee = txbuilder.CalculateTxFee(&ins.Tx)
295 return NewSuccessResponse(tx)
298 // POST /list-unspent-outputs
299 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
300 AccountID string `json:"account_id"`
301 AccountAlias string `json:"account_alias"`
302 ID string `json:"id"`
303 Unconfirmed bool `json:"unconfirmed"`
304 SmartContract bool `json:"smart_contract"`
305 From uint `json:"from"`
306 Count uint `json:"count"`
308 accountID := filter.AccountID
309 if filter.AccountAlias != "" {
310 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
312 return NewErrorResponse(err)
316 accountUTXOs := a.wallet.GetAccountUtxos(accountID, filter.ID, filter.Unconfirmed, filter.SmartContract, false)
318 UTXOs := []query.AnnotatedUTXO{}
319 for _, utxo := range accountUTXOs {
320 UTXOs = append([]query.AnnotatedUTXO{{
321 AccountID: utxo.AccountID,
322 OutputID: utxo.OutputID.String(),
323 SourceID: utxo.SourceID.String(),
324 AssetID: utxo.AssetID.String(),
326 SourcePos: utxo.SourcePos,
327 Program: fmt.Sprintf("%x", utxo.ControlProgram),
328 ControlProgramIndex: utxo.ControlProgramIndex,
329 Address: utxo.Address,
330 ValidHeight: utxo.ValidHeight,
331 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
332 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
336 start, end := getPageRange(len(UTXOs), filter.From, filter.Count)
337 return NewSuccessResponse(UTXOs[start:end])
341 func (a *API) gasRate() Response {
342 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
343 return NewSuccessResponse(gasrate)
346 // PubKeyInfo is structure of pubkey info
347 type PubKeyInfo struct {
348 Pubkey string `json:"pubkey"`
349 Path []chainjson.HexBytes `json:"derivation_path"`
352 // AccountPubkey is detail of account pubkey info
353 type AccountPubkey struct {
354 RootXPub chainkd.XPub `json:"root_xpub"`
355 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
358 func getPubkey(account *account.Account, change bool, index uint64) (*ed25519.PublicKey, []chainjson.HexBytes, error) {
359 rawPath, err := signers.Path(account.Signer, signers.AccountKeySpace, change, index)
363 derivedXPub := account.XPubs[0].Derive(rawPath)
364 pubkey := derivedXPub.PublicKey()
365 var path []chainjson.HexBytes
366 for _, p := range rawPath {
367 path = append(path, chainjson.HexBytes(p))
370 return &pubkey, path, nil
373 // POST /list-pubkeys
374 func (a *API) listPubKeys(ctx context.Context, ins struct {
375 AccountID string `json:"account_id"`
376 AccountAlias string `json:"account_alias"`
377 PublicKey string `json:"public_key"`
380 account := &account.Account{}
381 if ins.AccountAlias != "" {
382 account, err = a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
384 account, err = a.wallet.AccountMgr.FindByID(ins.AccountID)
388 return NewErrorResponse(err)
391 pubKeyInfos := []PubKeyInfo{}
392 if account.DeriveRule == signers.BIP0032 {
393 idx := a.wallet.AccountMgr.GetContractIndex(account.ID)
394 for i := uint64(1); i <= idx; i++ {
395 pubkey, path, err := getPubkey(account, false, i)
397 return NewErrorResponse(err)
399 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
402 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
403 Pubkey: hex.EncodeToString(*pubkey),
407 } else if account.DeriveRule == signers.BIP0044 {
408 idx := a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, true)
409 for i := uint64(1); i <= idx; i++ {
410 pubkey, path, err := getPubkey(account, true, i)
412 return NewErrorResponse(err)
414 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
417 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
418 Pubkey: hex.EncodeToString(*pubkey),
423 idx = a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, false)
424 for i := uint64(1); i <= idx; i++ {
425 pubkey, path, err := getPubkey(account, false, i)
427 return NewErrorResponse(err)
429 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
432 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
433 Pubkey: hex.EncodeToString(*pubkey),
439 if len(pubKeyInfos) == 0 {
440 return NewErrorResponse(errors.New("Not found publickey for the account"))
443 return NewSuccessResponse(&AccountPubkey{
444 RootXPub: account.XPubs[0],
445 PubKeyInfos: pubKeyInfos,