github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/api/block_retrieve.go (about)

     1  package api
     2  
     3  import (
     4  	"gopkg.in/fatih/set.v0"
     5  
     6  	"github.com/bytom/bytom/blockchain/query"
     7  	chainjson "github.com/bytom/bytom/encoding/json"
     8  	"github.com/bytom/bytom/protocol/bc"
     9  	"github.com/bytom/bytom/protocol/bc/types"
    10  )
    11  
    12  // return best block hash
    13  func (a *API) getBestBlockHash() Response {
    14  	blockHash := map[string]string{"block_hash": a.chain.BestBlockHash().String()}
    15  	return NewSuccessResponse(blockHash)
    16  }
    17  
    18  // return current block count
    19  func (a *API) getBlockCount() Response {
    20  	blockHeight := map[string]uint64{"block_count": a.chain.BestBlockHeight()}
    21  	return NewSuccessResponse(blockHeight)
    22  }
    23  
    24  // BlockTx is the tx struct for getBlock func
    25  type BlockTx struct {
    26  	ID        bc.Hash                  `json:"id"`
    27  	Version   uint64                   `json:"version"`
    28  	Size      uint64                   `json:"size"`
    29  	TimeRange uint64                   `json:"time_range"`
    30  	Inputs    []*query.AnnotatedInput  `json:"inputs"`
    31  	Outputs   []*query.AnnotatedOutput `json:"outputs"`
    32  	MuxID     bc.Hash                  `json:"mux_id"`
    33  }
    34  
    35  // BlockReq is used to handle getBlock req
    36  type BlockReq struct {
    37  	BlockHeight uint64             `json:"block_height"`
    38  	BlockHash   chainjson.HexBytes `json:"block_hash"`
    39  }
    40  
    41  // GetBlockResp is the resp for getBlock api
    42  type GetBlockResp struct {
    43  	Hash                   *bc.Hash   `json:"hash"`
    44  	Size                   uint64     `json:"size"`
    45  	Version                uint64     `json:"version"`
    46  	Height                 uint64     `json:"height"`
    47  	Validator              string     `json:"validator"`
    48  	PreviousBlockHash      *bc.Hash   `json:"previous_block_hash"`
    49  	Timestamp              uint64     `json:"timestamp"`
    50  	TransactionsMerkleRoot *bc.Hash   `json:"transaction_merkle_root"`
    51  	Transactions           []*BlockTx `json:"transactions"`
    52  }
    53  
    54  // return block by hash/height
    55  func (a *API) getBlock(ins BlockReq) Response {
    56  	block, err := a.getBlockHelper(ins)
    57  	if err != nil {
    58  		return NewErrorResponse(err)
    59  	}
    60  
    61  	blockHash := block.Hash()
    62  	rawBlock, err := block.MarshalText()
    63  	if err != nil {
    64  		return NewErrorResponse(err)
    65  	}
    66  
    67  	var validatorPubKey string
    68  	if block.Height > 0 {
    69  		validator, err := a.chain.GetValidator(&block.PreviousBlockHash, block.Timestamp)
    70  		if err != nil {
    71  			return NewErrorResponse(err)
    72  		}
    73  
    74  		validatorPubKey = validator.PubKey
    75  	}
    76  
    77  	resp := &GetBlockResp{
    78  		Hash:                   &blockHash,
    79  		Size:                   uint64(len(rawBlock)),
    80  		Version:                block.Version,
    81  		Height:                 block.Height,
    82  		Validator:              validatorPubKey,
    83  		PreviousBlockHash:      &block.PreviousBlockHash,
    84  		Timestamp:              block.Timestamp,
    85  		TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
    86  		Transactions:           []*BlockTx{},
    87  	}
    88  
    89  	for _, orig := range block.Transactions {
    90  		tx := &BlockTx{
    91  			ID:        orig.ID,
    92  			Version:   orig.Version,
    93  			Size:      orig.SerializedSize,
    94  			TimeRange: orig.TimeRange,
    95  			Inputs:    []*query.AnnotatedInput{},
    96  			Outputs:   []*query.AnnotatedOutput{},
    97  		}
    98  
    99  		resOutID := orig.ResultIds[0]
   100  		resOut := orig.Entries[*resOutID]
   101  		switch out :=resOut.(type) {
   102  		case *bc.OriginalOutput:
   103  			tx.MuxID = *out.Source.Ref
   104  		case *bc.VoteOutput:
   105  			tx.MuxID = *out.Source.Ref
   106  		case *bc.Retirement:
   107  			tx.MuxID = *out.Source.Ref
   108  		}
   109  
   110  		for i := range orig.Inputs {
   111  			tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(orig, uint32(i)))
   112  		}
   113  		for i := range orig.Outputs {
   114  			tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(orig, i))
   115  		}
   116  		resp.Transactions = append(resp.Transactions, tx)
   117  	}
   118  	return NewSuccessResponse(resp)
   119  }
   120  
   121  // GetRawBlockResp is resp struct for getRawBlock API
   122  type GetRawBlockResp struct {
   123  	RawBlock  *types.Block `json:"raw_block"`
   124  	Validator string       `json:"validator"`
   125  }
   126  
   127  func (a *API) getRawBlock(ins BlockReq) Response {
   128  	block, err := a.getBlockHelper(ins)
   129  	if err != nil {
   130  		return NewErrorResponse(err)
   131  	}
   132  
   133  	var validatorPubKey string
   134  	if block.Height > 0 {
   135  		validator, err := a.chain.GetValidator(&block.PreviousBlockHash, block.Timestamp)
   136  		if err != nil {
   137  			return NewErrorResponse(err)
   138  		}
   139  
   140  		validatorPubKey = validator.PubKey
   141  	}
   142  
   143  	resp := GetRawBlockResp{
   144  		RawBlock:  block,
   145  		Validator: validatorPubKey,
   146  	}
   147  	return NewSuccessResponse(resp)
   148  }
   149  
   150  // GetBlockHeaderResp is resp struct for getBlockHeader API
   151  type GetBlockHeaderResp struct {
   152  	BlockHeader *types.BlockHeader `json:"block_header"`
   153  }
   154  
   155  func (a *API) getBlockHeader(ins BlockReq) Response {
   156  	block, err := a.getBlockHelper(ins)
   157  	if err != nil {
   158  		return NewErrorResponse(err)
   159  	}
   160  
   161  	resp := &GetBlockHeaderResp{
   162  		BlockHeader: &block.BlockHeader,
   163  	}
   164  	return NewSuccessResponse(resp)
   165  }
   166  
   167  func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
   168  	if len(ins.BlockHash) == 32 {
   169  		hash := hexBytesToHash(ins.BlockHash)
   170  		return a.chain.GetBlockByHash(&hash)
   171  	} else {
   172  		return a.chain.GetBlockByHeight(ins.BlockHeight)
   173  	}
   174  }
   175  
   176  func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
   177  	b32 := [32]byte{}
   178  	copy(b32[:], hexBytes)
   179  	return bc.NewHash(b32)
   180  }
   181  
   182  // MerkleBlockReq is used to handle getTxOutProof req
   183  type MerkleBlockReq struct {
   184  	TxIDs     []chainjson.HexBytes `json:"tx_ids"`
   185  	BlockHash chainjson.HexBytes   `json:"block_hash"`
   186  }
   187  
   188  // GetMerkleBlockResp is resp struct for GetTxOutProof API
   189  type GetMerkleBlockResp struct {
   190  	BlockHeader  types.BlockHeader `json:"block_header"`
   191  	TxHashes     []*bc.Hash        `json:"tx_hashes"`
   192  	Flags        []uint32          `json:"flags"`
   193  	MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
   194  }
   195  
   196  func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
   197  	blockReq := BlockReq{BlockHash: ins.BlockHash}
   198  	block, err := a.getBlockHelper(blockReq)
   199  	if err != nil {
   200  		return NewErrorResponse(err)
   201  	}
   202  
   203  	matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
   204  	var matchedTxIDs []*bc.Hash
   205  	for _, tx := range matchedTxs {
   206  		matchedTxIDs = append(matchedTxIDs, &tx.ID)
   207  	}
   208  
   209  	hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
   210  	flags := make([]uint32, len(compactFlags))
   211  	for i, flag := range compactFlags {
   212  		flags[i] = uint32(flag)
   213  	}
   214  
   215  	resp := &GetMerkleBlockResp{
   216  		BlockHeader:  block.BlockHeader,
   217  		TxHashes:     hashes,
   218  		Flags:        flags,
   219  		MatchedTxIDs: matchedTxIDs,
   220  	}
   221  	return NewSuccessResponse(resp)
   222  }
   223  
   224  func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
   225  	txIDSet := set.New()
   226  	for _, txID := range filterTxIDs {
   227  		hash := hexBytesToHash(txID)
   228  		txIDSet.Add(hash.String())
   229  	}
   230  
   231  	var matchedTxs []*types.Tx
   232  	for _, tx := range txs {
   233  		hashStr := tx.ID.String()
   234  		if txIDSet.Has(hashStr) {
   235  			matchedTxs = append(matchedTxs, tx)
   236  		}
   237  	}
   238  	return matchedTxs
   239  }