OSDN Git Service

update doc (#1807)
[bytom/bytom.git] / api / block_retrieve.go
index bd8d678..37642e2 100644 (file)
@@ -1,13 +1,16 @@
 package api
 
 import (
-       log "github.com/sirupsen/logrus"
+       "math/big"
 
-       "github.com/bytom/blockchain/query"
-       "github.com/bytom/consensus/difficulty"
-       chainjson "github.com/bytom/encoding/json"
-       "github.com/bytom/protocol/bc"
-       "github.com/bytom/protocol/bc/types"
+       "gopkg.in/fatih/set.v0"
+
+       "github.com/bytom/bytom/blockchain/query"
+       "github.com/bytom/bytom/consensus/difficulty"
+       chainjson "github.com/bytom/bytom/encoding/json"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/protocol/bc"
+       "github.com/bytom/bytom/protocol/bc/types"
 )
 
 // return best block hash
@@ -16,27 +19,10 @@ func (a *API) getBestBlockHash() Response {
        return NewSuccessResponse(blockHash)
 }
 
-// return block header by hash
-func (a *API) getBlockHeaderByHash(req struct {
-       BlockHash string `json:"block_hash"`
-}) Response {
-       hash := bc.Hash{}
-       if err := hash.UnmarshalText([]byte(req.BlockHash)); err != nil {
-               log.WithField("error", err).Error("Error occurs when transforming string hash to hash struct")
-               return NewErrorResponse(err)
-       }
-       block, err := a.chain.GetBlockByHash(&hash)
-       if err != nil {
-               log.WithField("error", err).Error("Fail to get block by hash")
-               return NewErrorResponse(err)
-       }
-
-       resp := &BlockHeaderByHeight{
-               BlockHeader: &block.BlockHeader,
-               Reward:      block.Transactions[0].Outputs[0].Amount,
-       }
-
-       return NewSuccessResponse(resp)
+// return current block count
+func (a *API) getBlockCount() Response {
+       blockHeight := map[string]uint64{"block_count": a.chain.BestBlockHeight()}
+       return NewSuccessResponse(blockHeight)
 }
 
 // BlockTx is the tx struct for getBlock func
@@ -48,10 +34,11 @@ type BlockTx struct {
        Inputs     []*query.AnnotatedInput  `json:"inputs"`
        Outputs    []*query.AnnotatedOutput `json:"outputs"`
        StatusFail bool                     `json:"status_fail"`
+       MuxID      bc.Hash                  `json:"mux_id"`
 }
 
-// GetBlockReq is used to handle getBlock req
-type GetBlockReq struct {
+// BlockReq is used to handle getBlock req
+type BlockReq struct {
        BlockHeight uint64             `json:"block_height"`
        BlockHash   chainjson.HexBytes `json:"block_hash"`
 }
@@ -72,18 +59,9 @@ type GetBlockResp struct {
        Transactions           []*BlockTx `json:"transactions"`
 }
 
-// return block by hash
-func (a *API) getBlock(ins GetBlockReq) Response {
-       var err error
-       block := &types.Block{}
-       if len(ins.BlockHash) == 32 {
-               b32 := [32]byte{}
-               copy(b32[:], ins.BlockHash)
-               hash := bc.NewHash(b32)
-               block, err = a.chain.GetBlockByHash(&hash)
-       } else {
-               block, err = a.chain.GetBlockByHeight(ins.BlockHeight)
-       }
+// return block by hash/height
+func (a *API) getBlock(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
        if err != nil {
                return NewErrorResponse(err)
        }
@@ -104,7 +82,7 @@ func (a *API) getBlock(ins GetBlockReq) Response {
                Timestamp:              block.Timestamp,
                Nonce:                  block.Nonce,
                Bits:                   block.Bits,
-               Difficulty:             difficulty.CompactToBig(block.Bits).String(),
+               Difficulty:             difficulty.CalcWork(block.Bits).String(),
                TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
                TransactionStatusHash:  &block.TransactionStatusHash,
                Transactions:           []*BlockTx{},
@@ -121,7 +99,16 @@ func (a *API) getBlock(ins GetBlockReq) Response {
                }
                tx.StatusFail, err = txStatus.GetStatus(i)
                if err != nil {
-                       NewSuccessResponse(resp)
+                       return NewSuccessResponse(resp)
+               }
+
+               resOutID := orig.ResultIds[0]
+               resOut, ok := orig.Entries[*resOutID].(*bc.Output)
+               if ok {
+                       tx.MuxID = *resOut.Source.Ref
+               } else {
+                       resRetire, _ := orig.Entries[*resOutID].(*bc.Retirement)
+                       tx.MuxID = *resRetire.Source.Ref
                }
 
                for i := range orig.Inputs {
@@ -135,8 +122,196 @@ func (a *API) getBlock(ins GetBlockReq) Response {
        return NewSuccessResponse(resp)
 }
 
-// return current block count
-func (a *API) getBlockCount() Response {
-       blockHeight := map[string]uint64{"block_count": a.chain.BestBlockHeight()}
-       return NewSuccessResponse(blockHeight)
+// GetRawBlockResp is resp struct for getRawBlock API
+type GetRawBlockResp struct {
+       RawBlock          *types.Block          `json:"raw_block"`
+       TransactionStatus *bc.TransactionStatus `json:"transaction_status"`
+}
+
+func (a *API) getRawBlock(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       blockHash := block.Hash()
+       txStatus, err := a.chain.GetTransactionStatus(&blockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       resp := GetRawBlockResp{
+               RawBlock:          block,
+               TransactionStatus: txStatus,
+       }
+       return NewSuccessResponse(resp)
+}
+
+// GetBlockHeaderResp is resp struct for getBlockHeader API
+type GetBlockHeaderResp struct {
+       BlockHeader *types.BlockHeader `json:"block_header"`
+       Reward      uint64             `json:"reward"`
+}
+
+func (a *API) getBlockHeader(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       resp := &GetBlockHeaderResp{
+               BlockHeader: &block.BlockHeader,
+               Reward:      block.Transactions[0].Outputs[0].Amount,
+       }
+       return NewSuccessResponse(resp)
+}
+
+func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
+       if len(ins.BlockHash) == 32 {
+               hash := hexBytesToHash(ins.BlockHash)
+               return a.chain.GetBlockByHash(&hash)
+       } else {
+               return a.chain.GetBlockByHeight(ins.BlockHeight)
+       }
+}
+
+func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
+       b32 := [32]byte{}
+       copy(b32[:], hexBytes)
+       return bc.NewHash(b32)
+}
+
+// GetDifficultyResp is resp struct for getDifficulty API
+type GetDifficultyResp struct {
+       BlockHash   *bc.Hash `json:"hash"`
+       BlockHeight uint64   `json:"height"`
+       Bits        uint64   `json:"bits"`
+       Difficulty  string   `json:"difficulty"`
+}
+
+func (a *API) getDifficulty(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       blockHash := block.Hash()
+       resp := &GetDifficultyResp{
+               BlockHash:   &blockHash,
+               BlockHeight: block.Height,
+               Bits:        block.Bits,
+               Difficulty:  difficulty.CalcWork(block.Bits).String(),
+       }
+       return NewSuccessResponse(resp)
+}
+
+// getHashRateResp is resp struct for getHashRate API
+type getHashRateResp struct {
+       BlockHash   *bc.Hash `json:"hash"`
+       BlockHeight uint64   `json:"height"`
+       HashRate    uint64   `json:"hash_rate"`
+}
+
+func (a *API) getHashRate(ins BlockReq) Response {
+       if len(ins.BlockHash) != 32 && len(ins.BlockHash) != 0 {
+               err := errors.New("Block hash format error.")
+               return NewErrorResponse(err)
+       }
+       if ins.BlockHeight == 0 {
+               ins.BlockHeight = a.chain.BestBlockHeight()
+       }
+
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       preBlock, err := a.chain.GetBlockByHash(&block.PreviousBlockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       diffTime := block.Timestamp - preBlock.Timestamp
+       if preBlock.Timestamp >= block.Timestamp {
+               diffTime = 1
+       }
+       hashCount := difficulty.CalcWork(block.Bits)
+       hashRate := new(big.Int).Div(hashCount, big.NewInt(int64(diffTime)))
+
+       blockHash := block.Hash()
+       resp := &getHashRateResp{
+               BlockHash:   &blockHash,
+               BlockHeight: block.Height,
+               HashRate:    hashRate.Uint64(),
+       }
+       return NewSuccessResponse(resp)
+}
+
+// MerkleBlockReq is used to handle getTxOutProof req
+type MerkleBlockReq struct {
+       TxIDs     []chainjson.HexBytes `json:"tx_ids"`
+       BlockHash chainjson.HexBytes   `json:"block_hash"`
+}
+
+// GetMerkleBlockResp is resp struct for GetTxOutProof API
+type GetMerkleBlockResp struct {
+       BlockHeader  types.BlockHeader `json:"block_header"`
+       TxHashes     []*bc.Hash        `json:"tx_hashes"`
+       StatusHashes []*bc.Hash        `json:"status_hashes"`
+       Flags        []uint32          `json:"flags"`
+       MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
+}
+
+func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
+       blockReq := BlockReq{BlockHash: ins.BlockHash}
+       block, err := a.getBlockHelper(blockReq)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
+       var matchedTxIDs []*bc.Hash
+       for _, tx := range matchedTxs {
+               matchedTxIDs = append(matchedTxIDs, &tx.ID)
+       }
+
+       hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
+       flags := make([]uint32, len(compactFlags))
+       for i, flag := range compactFlags {
+               flags[i] = uint32(flag)
+       }
+
+       blockHash := block.Hash()
+       statuses, err := a.chain.GetTransactionStatus(&blockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       statusHashes := types.GetStatusMerkleTreeProof(statuses.VerifyStatus, compactFlags)
+
+       resp := &GetMerkleBlockResp{
+               BlockHeader:  block.BlockHeader,
+               TxHashes:     hashes,
+               StatusHashes: statusHashes,
+               Flags:        flags,
+               MatchedTxIDs: matchedTxIDs,
+       }
+       return NewSuccessResponse(resp)
+}
+
+func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
+       txIDSet := set.New()
+       for _, txID := range filterTxIDs {
+               hash := hexBytesToHash(txID)
+               txIDSet.Add(hash.String())
+       }
+
+       var matchedTxs []*types.Tx
+       for _, tx := range txs {
+               hashStr := tx.ID.String()
+               if txIDSet.Has(hashStr) {
+                       matchedTxs = append(matchedTxs, tx)
+               }
+       }
+       return matchedTxs
 }