8 "github.com/vapor/blockchain/txbuilder"
9 "github.com/vapor/crypto/ed25519"
11 log "github.com/sirupsen/logrus"
13 "github.com/vapor/account"
14 "github.com/vapor/blockchain/query"
15 "github.com/vapor/blockchain/signers"
16 "github.com/vapor/consensus"
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"
22 bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
25 // POST /list-accounts
26 func (a *API) listAccounts(ctx context.Context, filter struct {
28 Alias string `json:"alias"`
30 accountID := filter.ID
31 if filter.Alias != "" {
32 acc, err := a.wallet.AccountMgr.FindByAlias(filter.Alias)
34 return NewErrorResponse(err)
39 accounts, err := a.wallet.AccountMgr.ListAccounts(accountID)
41 log.Errorf("listAccounts: %v", err)
42 return NewErrorResponse(err)
45 annotatedAccounts := []query.AnnotatedAccount{}
46 for _, acc := range accounts {
47 annotatedAccounts = append(annotatedAccounts, *account.Annotated(acc))
50 return NewSuccessResponse(annotatedAccounts)
54 func (a *API) getAsset(ctx context.Context, filter struct {
57 asset, err := a.wallet.AssetReg.GetAsset(filter.ID)
59 log.Errorf("getAsset: %v", err)
60 return NewErrorResponse(err)
63 return NewSuccessResponse(asset)
67 func (a *API) listAssets(ctx context.Context, filter struct {
70 assets, err := a.wallet.AssetReg.ListAssets(filter.ID)
72 log.Errorf("listAssets: %v", err)
73 return NewErrorResponse(err)
76 return NewSuccessResponse(assets)
79 // POST /list-balances
80 func (a *API) listBalances(ctx context.Context, filter struct {
81 AccountID string `json:"account_id"`
82 AccountAlias string `json:"account_alias"`
84 accountID := filter.AccountID
85 if filter.AccountAlias != "" {
86 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
88 return NewErrorResponse(err)
93 balances, err := a.wallet.GetAccountBalances(accountID, "")
95 return NewErrorResponse(err)
97 return NewSuccessResponse(balances)
100 // POST /get-transaction
101 func (a *API) getTransaction(ctx context.Context, txInfo struct {
102 TxID string `json:"tx_id"`
104 var annotatedTx *query.AnnotatedTx
107 annotatedTx, err = a.wallet.GetTransactionByTxID(txInfo.TxID)
109 // transaction not found in blockchain db, search it from unconfirmed db
110 annotatedTx, err = a.wallet.GetUnconfirmedTxByTxID(txInfo.TxID)
112 return NewErrorResponse(err)
116 return NewSuccessResponse(annotatedTx)
119 // POST /list-transactions
120 func (a *API) listTransactions(ctx context.Context, filter struct {
121 ID string `json:"id"`
122 AccountID string `json:"account_id"`
123 Detail bool `json:"detail"`
124 Unconfirmed bool `json:"unconfirmed"`
125 From uint `json:"from"`
126 Count uint `json:"count"`
128 transactions := []*query.AnnotatedTx{}
130 var transaction *query.AnnotatedTx
133 transaction, err = a.wallet.GetTransactionByTxID(filter.ID)
134 if err != nil && filter.Unconfirmed {
135 transaction, err = a.wallet.GetUnconfirmedTxByTxID(filter.ID)
137 return NewErrorResponse(err)
140 transactions = []*query.AnnotatedTx{transaction}
142 transactions, err = a.wallet.GetTransactions(filter.AccountID)
144 return NewErrorResponse(err)
147 if filter.Unconfirmed {
148 unconfirmedTxs, err := a.wallet.GetUnconfirmedTxs(filter.AccountID)
150 return NewErrorResponse(err)
152 transactions = append(unconfirmedTxs, transactions...)
156 if filter.Detail == false {
157 txSummary := a.wallet.GetTransactionsSummary(transactions)
158 start, end := getPageRange(len(txSummary), filter.From, filter.Count)
159 return NewSuccessResponse(txSummary[start:end])
161 start, end := getPageRange(len(transactions), filter.From, filter.Count)
162 return NewSuccessResponse(transactions[start:end])
165 // POST /get-unconfirmed-transaction
166 func (a *API) getUnconfirmedTx(ctx context.Context, filter struct {
167 TxID chainjson.HexBytes `json:"tx_id"`
170 copy(tmpTxID[:], filter.TxID[:])
172 txHash := bc.NewHash(tmpTxID)
173 txPool := a.chain.GetTxPool()
174 txDesc, err := txPool.GetTransaction(&txHash)
176 return NewErrorResponse(err)
181 Version: txDesc.Tx.Version,
182 Size: txDesc.Tx.SerializedSize,
183 TimeRange: txDesc.Tx.TimeRange,
184 Inputs: []*query.AnnotatedInput{},
185 Outputs: []*query.AnnotatedOutput{},
186 StatusFail: txDesc.StatusFail,
189 resOutID := txDesc.Tx.ResultIds[0]
190 resOut := txDesc.Tx.Entries[*resOutID]
191 switch out := resOut.(type) {
193 tx.MuxID = *out.Source.Ref
195 tx.MuxID = *out.Source.Ref
198 for i := range txDesc.Tx.Inputs {
199 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(txDesc.Tx, uint32(i)))
201 for i := range txDesc.Tx.Outputs {
202 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(txDesc.Tx, i))
205 return NewSuccessResponse(tx)
208 type unconfirmedTxsResp struct {
209 Total uint64 `json:"total"`
210 TxIDs []bc.Hash `json:"tx_ids"`
213 // POST /list-unconfirmed-transactions
214 func (a *API) listUnconfirmedTxs(ctx context.Context) Response {
217 txPool := a.chain.GetTxPool()
218 txs := txPool.GetTransactions()
219 for _, txDesc := range txs {
220 txIDs = append(txIDs, bc.Hash(txDesc.Tx.ID))
223 return NewSuccessResponse(&unconfirmedTxsResp{
224 Total: uint64(len(txIDs)),
229 // RawTx is the tx struct for getRawTransaction
231 ID bc.Hash `json:"tx_id"`
232 Version uint64 `json:"version"`
233 Size uint64 `json:"size"`
234 TimeRange uint64 `json:"time_range"`
235 Inputs []*query.AnnotatedInput `json:"inputs"`
236 Outputs []*query.AnnotatedOutput `json:"outputs"`
237 Fee uint64 `json:"fee"`
240 // POST /decode-raw-transaction
241 func (a *API) decodeRawTransaction(ctx context.Context, ins struct {
242 Tx types.Tx `json:"raw_transaction"`
246 Version: ins.Tx.Version,
247 Size: ins.Tx.SerializedSize,
248 TimeRange: ins.Tx.TimeRange,
249 Inputs: []*query.AnnotatedInput{},
250 Outputs: []*query.AnnotatedOutput{},
253 for i := range ins.Tx.Inputs {
254 tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(&ins.Tx, uint32(i)))
256 for i := range ins.Tx.Outputs {
257 tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(&ins.Tx, i))
260 tx.Fee = txbuilder.CalculateTxFee(&ins.Tx)
261 return NewSuccessResponse(tx)
264 // POST /list-unspent-outputs
265 func (a *API) listUnspentOutputs(ctx context.Context, filter struct {
266 AccountID string `json:"account_id"`
267 AccountAlias string `json:"account_alias"`
268 ID string `json:"id"`
269 Unconfirmed bool `json:"unconfirmed"`
270 SmartContract bool `json:"smart_contract"`
271 From uint `json:"from"`
272 Count uint `json:"count"`
274 accountID := filter.AccountID
275 if filter.AccountAlias != "" {
276 acc, err := a.wallet.AccountMgr.FindByAlias(filter.AccountAlias)
278 return NewErrorResponse(err)
282 accountUTXOs := a.wallet.GetAccountUtxos(accountID, filter.ID, filter.Unconfirmed, filter.SmartContract)
284 UTXOs := []query.AnnotatedUTXO{}
285 for _, utxo := range accountUTXOs {
286 UTXOs = append([]query.AnnotatedUTXO{{
287 AccountID: utxo.AccountID,
288 OutputID: utxo.OutputID.String(),
289 SourceID: utxo.SourceID.String(),
290 AssetID: utxo.AssetID.String(),
292 SourcePos: utxo.SourcePos,
293 Program: fmt.Sprintf("%x", utxo.ControlProgram),
294 ControlProgramIndex: utxo.ControlProgramIndex,
295 Address: utxo.Address,
296 ValidHeight: utxo.ValidHeight,
297 Alias: a.wallet.AccountMgr.GetAliasByID(utxo.AccountID),
298 AssetAlias: a.wallet.AssetReg.GetAliasByID(utxo.AssetID.String()),
302 start, end := getPageRange(len(UTXOs), filter.From, filter.Count)
303 return NewSuccessResponse(UTXOs[start:end])
307 func (a *API) gasRate() Response {
308 gasrate := map[string]int64{"gas_rate": consensus.VMGasRate}
309 return NewSuccessResponse(gasrate)
312 // PubKeyInfo is structure of pubkey info
313 type PubKeyInfo struct {
314 Pubkey string `json:"pubkey"`
315 Path []chainjson.HexBytes `json:"derivation_path"`
318 // AccountPubkey is detail of account pubkey info
319 type AccountPubkey struct {
320 RootXPub chainkd.XPub `json:"root_xpub"`
321 PubKeyInfos []PubKeyInfo `json:"pubkey_infos"`
324 func getPubkey(account *account.Account, change bool, index uint64) (*ed25519.PublicKey, []chainjson.HexBytes, error) {
325 rawPath, err := signers.Path(account.Signer, signers.AccountKeySpace, change, index)
329 derivedXPub := account.XPubs[0].Derive(rawPath)
330 pubkey := derivedXPub.PublicKey()
331 var path []chainjson.HexBytes
332 for _, p := range rawPath {
333 path = append(path, chainjson.HexBytes(p))
336 return &pubkey, path, nil
339 // POST /list-pubkeys
340 func (a *API) listPubKeys(ctx context.Context, ins struct {
341 AccountID string `json:"account_id"`
342 AccountAlias string `json:"account_alias"`
343 PublicKey string `json:"public_key"`
346 account := &account.Account{}
347 if ins.AccountAlias != "" {
348 account, err = a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
350 account, err = a.wallet.AccountMgr.FindByID(ins.AccountID)
354 return NewErrorResponse(err)
357 pubKeyInfos := []PubKeyInfo{}
358 if account.DeriveRule == signers.BIP0032 {
359 idx := a.wallet.AccountMgr.GetContractIndex(account.ID)
360 for i := uint64(1); i <= idx; i++ {
361 pubkey, path, err := getPubkey(account, false, i)
363 return NewErrorResponse(err)
365 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
368 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
369 Pubkey: hex.EncodeToString(*pubkey),
373 } else if account.DeriveRule == signers.BIP0044 {
374 idx := a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, true)
375 for i := uint64(1); i <= idx; i++ {
376 pubkey, path, err := getPubkey(account, true, i)
378 return NewErrorResponse(err)
380 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
383 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
384 Pubkey: hex.EncodeToString(*pubkey),
389 idx = a.wallet.AccountMgr.GetBip44ContractIndex(account.ID, false)
390 for i := uint64(1); i <= idx; i++ {
391 pubkey, path, err := getPubkey(account, false, i)
393 return NewErrorResponse(err)
395 if ins.PublicKey != "" && ins.PublicKey != hex.EncodeToString(*pubkey) {
398 pubKeyInfos = append(pubKeyInfos, PubKeyInfo{
399 Pubkey: hex.EncodeToString(*pubkey),
405 if len(pubKeyInfos) == 0 {
406 return NewErrorResponse(errors.New("Not found publickey for the account"))
409 return NewSuccessResponse(&AccountPubkey{
410 RootXPub: account.XPubs[0],
411 PubKeyInfos: pubKeyInfos,
415 type GetRawTransationResp struct {
416 Tx *bytomtypes.Tx `json:"raw_transaction"`
417 BlockHash bc.Hash `json:"block_hash"`
420 func (a *API) getRawTransaction(ins struct {
421 RawBlock string `json:"raw_block"`
422 TxID string `json:"tx_id"`
425 var rawTransaction *bytomtypes.Tx
426 block := &bytomtypes.Block{}
427 err := block.UnmarshalText([]byte(ins.RawBlock))
429 return NewErrorResponse(err)
433 txID.UnmarshalText([]byte(ins.TxID))
434 for _, tx := range block.Transactions {
435 if tx.ID.String() == txID.String() {
440 if rawTransaction == nil {
441 return NewErrorResponse(errors.New("raw transaction do not find"))
443 resp := GetRawTransationResp{Tx: rawTransaction, BlockHash: block.Hash()}
444 return NewSuccessResponse(resp)
447 type GetSideRawTransationResp struct {
448 Tx *types.Tx `json:"raw_transaction"`
449 BlockHash bc.Hash `json:"block_hash"`
452 func (a *API) getSideRawTransaction(ins struct {
453 BlockHeight uint64 `json:"block_height"`
454 TxID string `json:"tx_id"`
456 block, err := a.chain.GetBlockByHeight(ins.BlockHeight)
458 return NewErrorResponse(err)
461 var rawTransaction *types.Tx
464 txID.UnmarshalText([]byte(ins.TxID))
466 for _, tx := range block.Transactions {
467 if tx.ID.String() == txID.String() {
472 if rawTransaction == nil {
473 return NewErrorResponse(errors.New("raw transaction do not find"))
475 resp := GetSideRawTransationResp{Tx: rawTransaction, BlockHash: block.Hash()}
476 return NewSuccessResponse(resp)
479 type utxoResp struct {
480 Utxo account.UTXO `json:"utxo"`
483 func (a *API) getUnspentOutputs(ins struct {
484 RawBlock string `json:"raw_block"`
485 TxID string `json:"tx_id"`
486 ID string `json:"id"`
487 Address string `json:"address"`
489 var rawTransaction *bytomtypes.Tx
490 block := &bytomtypes.Block{}
491 err := block.UnmarshalText([]byte(ins.RawBlock))
493 return NewErrorResponse(err)
497 txID.UnmarshalText([]byte(ins.TxID))
499 for _, tx := range block.Transactions {
500 if tx.ID.String() == txID.String() {
505 utxo := account.UTXO{}
507 for i, out := range rawTransaction.Outputs {
508 key := rawTransaction.ResultIds[i]
509 if key.String() == ins.ID {
510 outPut, err := rawTransaction.Output(*key)
521 assetID := bc.AssetID{
522 V0: out.AssetAmount.AssetId.GetV0(),
523 V1: out.AssetAmount.AssetId.GetV1(),
524 V2: out.AssetAmount.AssetId.GetV2(),
525 V3: out.AssetAmount.AssetId.GetV3(),
529 V0: outPut.Source.Ref.GetV0(),
530 V1: outPut.Source.Ref.GetV1(),
531 V2: outPut.Source.Ref.GetV2(),
532 V3: outPut.Source.Ref.GetV3(),
538 ControlProgram: out.ControlProgram,
540 SourcePos: outPut.Source.Position,
541 ValidHeight: block.Height,
542 Address: ins.Address,
547 return NewSuccessResponse(&utxoResp{Utxo: utxo})