}
}
```
+## 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=<integer>&limit=<integer>` 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
+ }
+}
+```
import (
"database/sql"
"fmt"
+ "strings"
"github.com/gin-gonic/gin"
// 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
}
}
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))
CrossTxPendingStatus
CrossTxCompletedStatus
)
+
+const (
+ CrossTxPendingStatusLabel = "pending"
+ CrossTxCompletedStatusLabel = "completed"
+)
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,
+ })
}