8 "github.com/vapor/blockchain/txbuilder"
9 "github.com/vapor/crypto/ed25519"
10 "github.com/vapor/protocol/bc/types/bytom"
12 log "github.com/sirupsen/logrus"
14 "github.com/vapor/account"
15 "github.com/vapor/blockchain/query"
16 "github.com/vapor/blockchain/signers"
17 "github.com/vapor/consensus"
18 "github.com/vapor/crypto/ed25519/chainkd"
19 chainjson "github.com/vapor/encoding/json"
20 "github.com/vapor/errors"
21 "github.com/vapor/protocol/bc"
22 "github.com/vapor/protocol/bc/types"
23 bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
26 // POST /list-accounts
27 func (a *API) listAccounts(ctx context.Context, filter struct {
29 Alias string `json:"alias"`
31 accountID := filter.ID
32 if filter.Alias != "" {
33 acc, err := a.wallet.AccountMgr.FindByAlias(filter.Alias)
35 return NewErrorResponse(err)
40 accounts, err := a.wallet.AccountMgr.ListAccounts(accountID)
42 log.Errorf("listAccounts: %v", err)
43 return NewErrorResponse(err)
46 annotatedAccounts := []query.AnnotatedAccount{}
47 for _, acc := range accounts {
48 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
51 return NewSuccessResponse(annotatedAccounts)
55 func (a *API) getAsset(ctx context.Context, filter struct {
58 asset, err := a.wallet.AssetReg.GetAsset(filter.ID)
60 log.Errorf("getAsset: %v", err)
61 return NewErrorResponse(err)
64 return NewSuccessResponse(asset)
68 func (a *API) listAssets(ctx context.Context, filter struct {
71 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
73 log.Errorf("listAssets: %v", err)
74 return NewErrorResponse(err)
77 return NewSuccessResponse(assets)
80 // POST /list-balances
81 func (a *API) listBalances(ctx context.Context, filter struct {
82 AccountID string `json:"account_id"`
83 AccountAlias string `json:"account_alias"`
85 accountID := filter.AccountID
86 if filter.AccountAlias != "" {
87 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
89 return NewErrorResponse(err)
94 balances, err := a.wallet.GetAccountBalances(accountID, "")
96 return NewErrorResponse(err)
98 return NewSuccessResponse(balances)
101 // POST /get-transaction
102 func (a *API) getTransaction(ctx context.Context, txInfo struct {
103 TxID string `json:"tx_id"`
105 var annotatedTx *query.AnnotatedTx
108 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
110 // transaction not found in blockchain db, search it from unconfirmed db
111 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
113 return NewErrorResponse(err)
117 return NewSuccessResponse(annotatedTx)
120 // POST /list-transactions
121 func (a *API) listTransactions(ctx context.Context, filter struct {
122 ID string `json:"id"`
123 AccountID string `json:"account_id"`
124 Detail bool `json:"detail"`
125 Unconfirmed bool `json:"unconfirmed"`
126 From uint `json:"from"`
127 Count uint `json:"count"`
129 transactions := []*query.AnnotatedTx{}
131 var transaction *query.AnnotatedTx
134 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
135 if err != nil && filter.Unconfirmed {
136 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
138 return NewErrorResponse(err)
141 transactions = []*query.AnnotatedTx{transaction}
143 transactions, err = a.wallet.GetTransactions(filter.AccountID)
145 return NewErrorResponse(err)
148 if filter.Unconfirmed {
149 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
151 return NewErrorResponse(err)
153 transactions = append(unconfirmedTxs, transactions...)
157 if filter.Detail == false {
158 txSummary := a.wallet.GetTransactionsSummary(transactions)
159 start, end := getPageRange(len(txSummary), filter.From, filter.Count)
160 return NewSuccessResponse(txSummary[start:end])
162 start, end := getPageRange(len(transactions), filter.From, filter.Count)
163 return NewSuccessResponse(transactions[start:end])
166 // POST /get-unconfirmed-transaction
167 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
168 TxID chainjson.HexBytes `json:"tx_id"`
171 copy(tmpTxID[:], filter.TxID[:])
173 txHash := bc.NewHash(tmpTxID)
174 txPool := a.chain.GetTxPool()
175 txDesc, err := txPool.GetTransaction(&txHash)
177 return NewErrorResponse(err)
182 Version: txDesc.Tx.Version,
183 Size: txDesc.Tx.SerializedSize,
184 TimeRange: txDesc.Tx.TimeRange,
185 Inputs: []*query.AnnotatedInput{},
186 Outputs: []*query.AnnotatedOutput{},
187 StatusFail: txDesc.StatusFail,
190 resOutID := txDesc.Tx.ResultIds[0]
191 resOut := txDesc.Tx.Entries[*resOutID]
192 switch out := resOut.(type) {
194 tx.MuxID = *out.Source.Ref
196 tx.MuxID = *out.Source.Ref
199 for i := range txDesc.Tx.Inputs {
200 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
202 for i := range txDesc.Tx.Outputs {
203 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
206 return NewSuccessResponse(tx)
209 type unconfirmedTxsResp struct {
210 Total uint64 `json:"total"`
211 TxIDs []bc.Hash `json:"tx_ids"`
214 // POST /list-unconfirmed-transactions
215 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
218 txPool := a.chain.GetTxPool()
219 txs := txPool.GetTransactions()
220 for _, txDesc := range txs {
221 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
224 return NewSuccessResponse(&unconfirmedTxsResp{
225 Total: uint64(len(txIDs)),
230 // RawTx is the tx struct for getRawTransaction
232 ID bc.Hash `json:"tx_id"`
233 Version uint64 `json:"version"`
234 Size uint64 `json:"size"`
235 TimeRange uint64 `json:"time_range"`
236 Inputs []*query.AnnotatedInput `json:"inputs"`
237 Outputs []*query.AnnotatedOutput `json:"outputs"`
238 Fee uint64 `json:"fee"`
241 // POST /decode-raw-transaction
242 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
243 Tx types.Tx `json:"raw_transaction"`
247 Version: ins.Tx.Version,
248 Size: ins.Tx.SerializedSize,
249 TimeRange: ins.Tx.TimeRange,
250 Inputs: []*query.AnnotatedInput{},
251 Outputs: []*query.AnnotatedOutput{},
254 for i := range ins.Tx.Inputs {
255 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
257 for i := range ins.Tx.Outputs {
258 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
261 tx.Fee = txbuilder.CalculateTxFee(&ins.Tx)
262 return NewSuccessResponse(tx)
265 // POST /list-unspent-outputs
266 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
267 AccountID string `json:"account_id"`
268 AccountAlias string `json:"account_alias"`
269 ID string `json:"id"`
270 Unconfirmed bool `json:"unconfirmed"`
271 SmartContract bool `json:"smart_contract"`
272 From uint `json:"from"`
273 Count uint `json:"count"`
275 accountID := filter.AccountID
276 if filter.AccountAlias != "" {
277 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
279 return NewErrorResponse(err)
283 accountUTXOs := a.wallet.GetAccountUtxos(accountID, filter.ID, filter.Unconfirmed, filter.SmartContract)
285 UTXOs := []query.AnnotatedUTXO{}
286 for _, utxo := range accountUTXOs {
287 UTXOs = append([]query.AnnotatedUTXO{{
288 AccountID: utxo.AccountID,
289 OutputID: utxo.OutputID.String(),
290 SourceID: utxo.SourceID.String(),
291 AssetID: utxo.AssetID.String(),
293 SourcePos: utxo.SourcePos,
294 Program: fmt.Sprintf("%x", utxo.ControlProgram),
295 ControlProgramIndex: utxo.ControlProgramIndex,
296 Address: utxo.Address,
297 ValidHeight: utxo.ValidHeight,
298 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
299 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
303 start, end := getPageRange(len(UTXOs), filter.From, filter.Count)
304 return NewSuccessResponse(UTXOs[start:end])
308 func (a *API) gasRate() Response {
309 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
310 return NewSuccessResponse(gasrate)
313 // PubKeyInfo is structure of pubkey info
314 type PubKeyInfo struct {
315 Pubkey string `json:"pubkey"`
316 Path []chainjson.HexBytes `json:"derivation_path"`
319 // AccountPubkey is detail of account pubkey info
320 type AccountPubkey struct {
321 RootXPub chainkd.XPub `json:"root_xpub"`
322 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
325 func getPubkey(account *account.Account, change bool, index uint64) (*ed25519.PublicKey, []chainjson.HexBytes, error) {
326 rawPath, err := signers.Path(account.Signer, signers.AccountKeySpace, change, index)
330 derivedXPub := account.XPubs[0].Derive(rawPath)
331 pubkey := derivedXPub.PublicKey()
332 var path []chainjson.HexBytes
333 for _, p := range rawPath {
334 path = append(path, chainjson.HexBytes(p))
337 return &pubkey, path, nil
340 // POST /list-pubkeys
341 func (a *API) listPubKeys(ctx context.Context, ins struct {
342 AccountID string `json:"account_id"`
343 AccountAlias string `json:"account_alias"`
344 PublicKey string `json:"public_key"`
347 account := &account.Account{}
348 if ins.AccountAlias != "" {
349 account, err = a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
351 account, err = a.wallet.AccountMgr.FindByID(ins.AccountID)
355 return NewErrorResponse(err)
358 pubKeyInfos := []PubKeyInfo{}
359 if account.DeriveRule == signers.BIP0032 {
360 idx := a.wallet.AccountMgr.GetContractIndex(account.ID)
361 for i := uint64(1); i <= idx; i++ {
362 pubkey, path, err := getPubkey(account, false, i)
364 return NewErrorResponse(err)
366 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
369 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
370 Pubkey: hex.EncodeToString(*pubkey),
374 } else if account.DeriveRule == signers.BIP0044 {
375 idx := a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, true)
376 for i := uint64(1); i <= idx; i++ {
377 pubkey, path, err := getPubkey(account, true, i)
379 return NewErrorResponse(err)
381 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
384 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
385 Pubkey: hex.EncodeToString(*pubkey),
390 idx = a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, false)
391 for i := uint64(1); i <= idx; i++ {
392 pubkey, path, err := getPubkey(account, false, i)
394 return NewErrorResponse(err)
396 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
399 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
400 Pubkey: hex.EncodeToString(*pubkey),
406 if len(pubKeyInfos) == 0 {
407 return NewErrorResponse(errors.New("Not found publickey for the account"))
410 return NewSuccessResponse(&AccountPubkey{
411 RootXPub: account.XPubs[0],
412 PubKeyInfos: pubKeyInfos,
416 type GetRawTransationResp struct {
417 Tx *bytomtypes.Tx `json:"raw_transaction"`
418 BlockHash bytom.Hash `json:"block_hash"`
421 func (a *API) getRawTransaction(ins struct {
422 RawBlock string `json:"raw_block"`
423 TxID string `json:"tx_id"`
426 var rawTransaction *bytomtypes.Tx
427 block := &bytomtypes.Block{}
428 err := block.UnmarshalText([]byte(ins.RawBlock))
430 return NewErrorResponse(err)
434 txID.UnmarshalText([]byte(ins.TxID))
436 for _, tx := range block.Transactions {
437 if tx.ID.String() == txID.String() {
442 if rawTransaction == nil {
443 return NewErrorResponse(errors.New("raw transaction do not find"))
445 resp := GetRawTransationResp{Tx: rawTransaction, BlockHash: block.Hash()}
446 return NewSuccessResponse(resp)
449 type GetSideRawTransationResp struct {
450 Tx *types.Tx `json:"raw_transaction"`
451 BlockHash bc.Hash `json:"block_hash"`
454 func (a *API) getSideRawTransaction(ins struct {
455 BlockHeight uint64 `json:"block_height"`
456 TxID string `json:"tx_id"`
458 block, err := a.chain.GetBlockByHeight(ins.BlockHeight)
460 return NewErrorResponse(err)
463 var rawTransaction *types.Tx
466 txID.UnmarshalText([]byte(ins.TxID))
468 for _, tx := range block.Transactions {
469 if tx.ID.String() == txID.String() {
474 if rawTransaction == nil {
475 return NewErrorResponse(errors.New("raw transaction do not find"))
477 resp := GetSideRawTransationResp{Tx: rawTransaction, BlockHash: block.Hash()}
478 return NewSuccessResponse(resp)
481 type utxoResp struct {
482 Utxo account.UTXO `json:"utxo"`
485 func (a *API) getUnspentOutputs(ins struct {
486 RawBlock string `json:"raw_block"`
487 TxID string `json:"tx_id"`
488 ID string `json:"id"`
489 Address string `json:"address"`
491 var rawTransaction *bytomtypes.Tx
492 block := &bytomtypes.Block{}
493 err := block.UnmarshalText([]byte(ins.RawBlock))
495 return NewErrorResponse(err)
499 txID.UnmarshalText([]byte(ins.TxID))
501 for _, tx := range block.Transactions {
502 if tx.ID.String() == txID.String() {
507 utxo := account.UTXO{}
509 for i, out := range rawTransaction.Outputs {
510 key := rawTransaction.ResultIds[i]
511 if key.String() == ins.ID {
512 outPut, err := rawTransaction.Output(*key)
523 assetID := bc.AssetID{
524 V0: out.AssetAmount.AssetId.GetV0(),
525 V1: out.AssetAmount.AssetId.GetV1(),
526 V2: out.AssetAmount.AssetId.GetV2(),
527 V3: out.AssetAmount.AssetId.GetV3(),
531 V0: outPut.Source.Ref.GetV0(),
532 V1: outPut.Source.Ref.GetV1(),
533 V2: outPut.Source.Ref.GetV2(),
534 V3: outPut.Source.Ref.GetV3(),
540 ControlProgram: out.ControlProgram,
542 SourcePos: outPut.Source.Position,
543 ValidHeight: block.Height,
544 Address: ins.Address,
549 return NewSuccessResponse(&utxoResp{Utxo: utxo})