From ae938cffc5c02b208a91eada0b7e69ddaa8a352d Mon Sep 17 00:00:00 2001 From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Date: Wed, 26 Jun 2019 14:57:31 +0800 Subject: [PATCH] doc(federation): add /list-crosschain-txs docs (#222) * init * add fed list-txs doc * update * refactor reponse * update docs --- docs/federation/README-en.md | 115 +++++++++++++++++++++++++++ federation/api/handler.go | 9 ++- federation/common/const.go | 5 ++ federation/database/orm/cross_transaction.go | 75 +++++++++++++---- 4 files changed, 183 insertions(+), 21 deletions(-) diff --git a/docs/federation/README-en.md b/docs/federation/README-en.md index cbec0638..907ee83d 100644 --- a/docs/federation/README-en.md +++ b/docs/federation/README-en.md @@ -46,3 +46,118 @@ A `fed_cfg.json` would look like this: } } ``` +## API +A federation node can function as an api server for querying cross-chain transactions. + +The default JSON-RPC endpoint is: [http://host:port/api/v1/federation/](http://host:port/api/v1/federation/) + +The response contains some meta data of: + ++ success/error status, which can be told from `code` and `msg`; ++ pagination info, which can be told from `start`, `limit` and `_links` (`_links` is used to look up the preceding and the succeeding items); + +and looks like: +``` +{ + "code":200, + "msg":"", + "result":{ + "_links":{ + }, + "data":..., + "limit":10, + "start":0 + } +} +``` + +If a request succeed, `data` field contains the detailed result as an object or as an array of objects. + +### Pagination + +Append `?start=&limit=` to the url in order to use pagination. + +### Methods + +#### `/list-crosschain-txs` + +To list cross-chain transactions and filter the transactions. + +##### Parameters + + + +Optional: + +- `Object` - *filter*, transactions filter. + + Optional + * `String` - *status*, transactions status, which can be `pending` or `completed`. + * `String` - *from_chain*, transactions source chain, which can be `bytom` or `vapor`. + * `String` - *source_tx_hash*, souce transaction hash string. + * `String` - *dest_tx_hash*, destination transaction hash string. +- `Object` - *sort*, transactions sorter. + + Optional + * `String` - *order*, transactions order sorter, which can be `asc` or `desc`. + + +##### Returns + + +`Object`: + +- `String` - *from_chain*, source chain name of the cross-chain transaction. +- `Integer` - *source_block_height*, block height of the cross-chain transaction on the source chain. +- `String` - *source_block_hash*, block hash of the cross-chain transaction on the source chain. +- `Integer` - *source_tx_index*, transaction index in the source block. +- `String` - *source_tx_hash*, source transaction hash. +- `Integer` - *dest_block_height*, block height of the cross-chain transaction on the destination chain, `0` if `status` is `pending`. +- `String` - *dest_block_hash*, block hash of the cross-chain transaction on the destination chain, empty string if `status` is `pending`. +- `Integer` - *dest_tx_index*, transaction index in the destination block, `0` if `status` is `pending`. +- `String` - *dest_tx_hash*, destination transaction hash, empty string if `status` is `pending`. +- `String` - *status*, cross-chain transaction status, can be `pending` or `completed`. +- `Array of objects` - *crosschain_requests*, asset transfer details per request included in the cross-chain transaction. + + `Integer` - *amount*, asset transfer amount. + + `Object` - *asset*, asset detail. + * `String` - *asset_id*, asset id string. + +##### Example + +```js +// Request +curl -X POST 127.0.0.1:3000/api/v1/federation/list-crosschain-txs -d '{}' + +// Result +{ + "code":200, + "msg":"", + "result":{ + "_links":{ + + }, + "data":[ + { + "from_chain":"bytom", + "source_block_height":174, + "source_block_hash":"569a3a5a43910ea634a947fd092bb3085359db451235ae59c20daab4e4b0d274", + "source_tx_index":1, + "source_tx_hash":"584d1dcc4dfe741bb3ae5b193896b08db469169e6fd76098eac132af628a3183", + "dest_block_height":0, + "dest_block_hash":"", + "dest_tx_index":0, + "dest_tx_hash":"", + "status":"pending", + "crosschain_requests":[ + { + "amount":1000000, + "asset":{ + "asset_id":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } + ] + } + ], + "limit":10, + "start":0 + } +} +``` diff --git a/federation/api/handler.go b/federation/api/handler.go index 0ea4b070..afd4bfbd 100644 --- a/federation/api/handler.go +++ b/federation/api/handler.go @@ -3,6 +3,7 @@ package api import ( "database/sql" "fmt" + "strings" "github.com/gin-gonic/gin" @@ -19,10 +20,10 @@ func (s *Server) ListCrosschainTxs(c *gin.Context, listTxsReq *listCrosschainTxs // filter tx status if status, err := listTxsReq.GetFilterString("status"); err == nil && status != "" { - switch status { - case "pending": + switch strings.ToLower(status) { + case common.CrossTxPendingStatusLabel: txFilter.Status = common.CrossTxPendingStatus - case "completed": + case common.CrossTxCompletedStatusLabel: txFilter.Status = common.CrossTxCompletedStatus } } @@ -37,7 +38,7 @@ func (s *Server) ListCrosschainTxs(c *gin.Context, listTxsReq *listCrosschainTxs txQuery := s.db.Preload("Chain").Preload("Reqs").Preload("Reqs.Asset").Where(txFilter) // filter direction - if fromChainName, err := listTxsReq.GetFilterString("from"); err == nil && fromChainName != "" { + if fromChainName, err := listTxsReq.GetFilterString("from_chain"); err == nil && fromChainName != "" { txQuery = txQuery.Joins("join chains on chains.id = cross_transactions.chain_id").Where("chains.name = ?", fromChainName) } txQuery = txQuery.Order(fmt.Sprintf("cross_transactions.source_block_height %s", listTxsReq.Sorter.Order)) diff --git a/federation/common/const.go b/federation/common/const.go index 94020315..56f7d0b3 100644 --- a/federation/common/const.go +++ b/federation/common/const.go @@ -5,3 +5,8 @@ const ( CrossTxPendingStatus CrossTxCompletedStatus ) + +const ( + CrossTxPendingStatusLabel = "pending" + CrossTxCompletedStatusLabel = "completed" +) diff --git a/federation/database/orm/cross_transaction.go b/federation/database/orm/cross_transaction.go index 9a70d9e6..4a327b8c 100644 --- a/federation/database/orm/cross_transaction.go +++ b/federation/database/orm/cross_transaction.go @@ -2,27 +2,68 @@ package orm import ( "database/sql" + "encoding/json" + "github.com/vapor/errors" + "github.com/vapor/federation/common" "github.com/vapor/federation/types" ) type CrossTransaction struct { - ID uint64 `gorm:"primary_key" json:"-"` - ChainID uint64 `json:"-"` - SourceBlockHeight uint64 `json:"source_block_height"` - SourceBlockHash string `json:"source_block_hash"` - SourceTxIndex uint64 `json:"source_tx_index"` - SourceMuxID string `json:"-"` - SourceTxHash string `json:"source_tx_hash"` - SourceRawTransaction string `json:"-"` - DestBlockHeight sql.NullInt64 `sql:"default:null" json:"dest_block_height"` - DestBlockHash sql.NullString `sql:"default:null" json:"dest_block_hash"` - DestTxIndex sql.NullInt64 `sql:"default:null" json:"dest_tx_index"` - DestTxHash sql.NullString `sql:"default:null" json:"dest_tx_hash"` - Status uint8 `json:"status"` - CreatedAt types.Timestamp `json:"-"` - UpdatedAt types.Timestamp `json:"-"` + ID uint64 `gorm:"primary_key"` + ChainID uint64 + SourceBlockHeight uint64 + SourceBlockHash string + SourceTxIndex uint64 + SourceMuxID string + SourceTxHash string + SourceRawTransaction string + DestBlockHeight sql.NullInt64 `sql:"default:null"` + DestBlockHash sql.NullString `sql:"default:null"` + DestTxIndex sql.NullInt64 `sql:"default:null"` + DestTxHash sql.NullString `sql:"default:null"` + Status uint8 + CreatedAt types.Timestamp + UpdatedAt types.Timestamp - Chain *Chain `gorm:"foreignkey:ChainID" json:"-"` - Reqs []*CrossTransactionReq `json:"crosschain_requests"` + Chain *Chain `gorm:"foreignkey:ChainID"` + Reqs []*CrossTransactionReq +} + +func (c *CrossTransaction) MarshalJSON() ([]byte, error) { + var status string + switch c.Status { + case common.CrossTxPendingStatus: + status = common.CrossTxPendingStatusLabel + case common.CrossTxCompletedStatus: + status = common.CrossTxCompletedStatusLabel + default: + return nil, errors.New("unknown cross-chain tx status") + } + + return json.Marshal(&struct { + FromChain string `json:"from_chain"` + SourceBlockHeight uint64 `json:"source_block_height"` + SourceBlockHash string `json:"source_block_hash"` + SourceTxIndex uint64 `json:"source_tx_index"` + SourceTxHash string `json:"source_tx_hash"` + DestBlockHeight uint64 `json:"dest_block_height"` + DestBlockHash string `json:"dest_block_hash"` + DestTxIndex uint64 `json:"dest_tx_index"` + DestTxHash string `json:"dest_tx_hash"` + Status string `json:"status"` + Reqs []*CrossTransactionReq `json:"crosschain_requests"` + }{ + FromChain: c.Chain.Name, + SourceBlockHeight: c.SourceBlockHeight, + SourceBlockHash: c.SourceBlockHash, + SourceTxIndex: c.SourceTxIndex, + SourceTxHash: c.SourceTxHash, + DestBlockHeight: uint64(c.DestBlockHeight.Int64), + DestBlockHash: c.DestBlockHash.String, + DestTxIndex: uint64(c.DestTxIndex.Int64), + DestTxHash: c.DestTxHash.String, + Status: status, + Reqs: c.Reqs, + }) } -- 2.11.0