github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/api/block_retrieve.go (about)

     1  package api
     2  
     3  import (
     4  	"math/big"
     5  
     6  	"gopkg.in/fatih/set.v0"
     7  
     8  	"github.com/bytom/bytom/blockchain/query"
     9  	"github.com/bytom/bytom/consensus/difficulty"
    10  	chainjson "github.com/bytom/bytom/encoding/json"
    11  	"github.com/bytom/bytom/errors"
    12  	"github.com/bytom/bytom/protocol/bc"
    13  	"github.com/bytom/bytom/protocol/bc/types"
    14  )
    15  
    16  // return best block hash
    17  func (a *API) getBestBlockHash() Response {
    18  	blockHash := map[string]string{"block_hash": a.chain.BestBlockHash().String()}
    19  	return NewSuccessResponse(blockHash)
    20  }
    21  
    22  // return current block count
    23  func (a *API) getBlockCount() Response {
    24  	blockHeight := map[string]uint64{"block_count": a.chain.BestBlockHeight()}
    25  	return NewSuccessResponse(blockHeight)
    26  }
    27  
    28  // BlockTx is the tx struct for getBlock func
    29  type BlockTx struct {
    30  	ID         bc.Hash                  `json:"id"`
    31  	Version    uint64                   `json:"version"`
    32  	Size       uint64                   `json:"size"`
    33  	TimeRange  uint64                   `json:"time_range"`
    34  	Inputs     []*query.AnnotatedInput  `json:"inputs"`
    35  	Outputs    []*query.AnnotatedOutput `json:"outputs"`
    36  	StatusFail bool                     `json:"status_fail"`
    37  	MuxID      bc.Hash                  `json:"mux_id"`
    38  }
    39  
    40  // BlockReq is used to handle getBlock req
    41  type BlockReq struct {
    42  	BlockHeight uint64             `json:"block_height"`
    43  	BlockHash   chainjson.HexBytes `json:"block_hash"`
    44  }
    45  
    46  // GetBlockResp is the resp for getBlock api
    47  type GetBlockResp struct {
    48  	Hash                   *bc.Hash   `json:"hash"`
    49  	Size                   uint64     `json:"size"`
    50  	Version                uint64     `json:"version"`
    51  	Height                 uint64     `json:"height"`
    52  	PreviousBlockHash      *bc.Hash   `json:"previous_block_hash"`
    53  	Timestamp              uint64     `json:"timestamp"`
    54  	Nonce                  uint64     `json:"nonce"`
    55  	Bits                   uint64     `json:"bits"`
    56  	Difficulty             string     `json:"difficulty"`
    57  	TransactionsMerkleRoot *bc.Hash   `json:"transaction_merkle_root"`
    58  	TransactionStatusHash  *bc.Hash   `json:"transaction_status_hash"`
    59  	Transactions           []*BlockTx `json:"transactions"`
    60  }
    61  
    62  // return block by hash/height
    63  func (a *API) getBlock(ins BlockReq) Response {
    64  	block, err := a.getBlockHelper(ins)
    65  	if err != nil {
    66  		return NewErrorResponse(err)
    67  	}
    68  
    69  	blockHash := block.Hash()
    70  	txStatus, err := a.chain.GetTransactionStatus(&blockHash)
    71  	rawBlock, err := block.MarshalText()
    72  	if err != nil {
    73  		return NewErrorResponse(err)
    74  	}
    75  
    76  	resp := &GetBlockResp{
    77  		Hash:                   &blockHash,
    78  		Size:                   uint64(len(rawBlock)),
    79  		Version:                block.Version,
    80  		Height:                 block.Height,
    81  		PreviousBlockHash:      &block.PreviousBlockHash,
    82  		Timestamp:              block.Timestamp,
    83  		Nonce:                  block.Nonce,
    84  		Bits:                   block.Bits,
    85  		Difficulty:             difficulty.CalcWork(block.Bits).String(),
    86  		TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
    87  		TransactionStatusHash:  &block.TransactionStatusHash,
    88  		Transactions:           []*BlockTx{},
    89  	}
    90  
    91  	for i, orig := range block.Transactions {
    92  		tx := &BlockTx{
    93  			ID:        orig.ID,
    94  			Version:   orig.Version,
    95  			Size:      orig.SerializedSize,
    96  			TimeRange: orig.TimeRange,
    97  			Inputs:    []*query.AnnotatedInput{},
    98  			Outputs:   []*query.AnnotatedOutput{},
    99  		}
   100  		tx.StatusFail, err = txStatus.GetStatus(i)
   101  		if err != nil {
   102  			return NewSuccessResponse(resp)
   103  		}
   104  
   105  		resOutID := orig.ResultIds[0]
   106  		resOut, ok := orig.Entries[*resOutID].(*bc.Output)
   107  		if ok {
   108  			tx.MuxID = *resOut.Source.Ref
   109  		} else {
   110  			resRetire, _ := orig.Entries[*resOutID].(*bc.Retirement)
   111  			tx.MuxID = *resRetire.Source.Ref
   112  		}
   113  
   114  		for i := range orig.Inputs {
   115  			tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(orig, uint32(i)))
   116  		}
   117  		for i := range orig.Outputs {
   118  			tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(orig, i))
   119  		}
   120  		resp.Transactions = append(resp.Transactions, tx)
   121  	}
   122  	return NewSuccessResponse(resp)
   123  }
   124  
   125  // GetRawBlockResp is resp struct for getRawBlock API
   126  type GetRawBlockResp struct {
   127  	RawBlock          *types.Block          `json:"raw_block"`
   128  	TransactionStatus *bc.TransactionStatus `json:"transaction_status"`
   129  }
   130  
   131  func (a *API) getRawBlock(ins BlockReq) Response {
   132  	block, err := a.getBlockHelper(ins)
   133  	if err != nil {
   134  		return NewErrorResponse(err)
   135  	}
   136  
   137  	blockHash := block.Hash()
   138  	txStatus, err := a.chain.GetTransactionStatus(&blockHash)
   139  	if err != nil {
   140  		return NewErrorResponse(err)
   141  	}
   142  
   143  	resp := GetRawBlockResp{
   144  		RawBlock:          block,
   145  		TransactionStatus: txStatus,
   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  	Reward      uint64             `json:"reward"`
   154  }
   155  
   156  func (a *API) getBlockHeader(ins BlockReq) Response {
   157  	block, err := a.getBlockHelper(ins)
   158  	if err != nil {
   159  		return NewErrorResponse(err)
   160  	}
   161  
   162  	resp := &GetBlockHeaderResp{
   163  		BlockHeader: &block.BlockHeader,
   164  		Reward:      block.Transactions[0].Outputs[0].Amount,
   165  	}
   166  	return NewSuccessResponse(resp)
   167  }
   168  
   169  func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
   170  	if len(ins.BlockHash) == 32 {
   171  		hash := hexBytesToHash(ins.BlockHash)
   172  		return a.chain.GetBlockByHash(&hash)
   173  	} else {
   174  		return a.chain.GetBlockByHeight(ins.BlockHeight)
   175  	}
   176  }
   177  
   178  func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
   179  	b32 := [32]byte{}
   180  	copy(b32[:], hexBytes)
   181  	return bc.NewHash(b32)
   182  }
   183  
   184  // GetDifficultyResp is resp struct for getDifficulty API
   185  type GetDifficultyResp struct {
   186  	BlockHash   *bc.Hash `json:"hash"`
   187  	BlockHeight uint64   `json:"height"`
   188  	Bits        uint64   `json:"bits"`
   189  	Difficulty  string   `json:"difficulty"`
   190  }
   191  
   192  func (a *API) getDifficulty(ins BlockReq) Response {
   193  	block, err := a.getBlockHelper(ins)
   194  	if err != nil {
   195  		return NewErrorResponse(err)
   196  	}
   197  
   198  	blockHash := block.Hash()
   199  	resp := &GetDifficultyResp{
   200  		BlockHash:   &blockHash,
   201  		BlockHeight: block.Height,
   202  		Bits:        block.Bits,
   203  		Difficulty:  difficulty.CalcWork(block.Bits).String(),
   204  	}
   205  	return NewSuccessResponse(resp)
   206  }
   207  
   208  // getHashRateResp is resp struct for getHashRate API
   209  type getHashRateResp struct {
   210  	BlockHash   *bc.Hash `json:"hash"`
   211  	BlockHeight uint64   `json:"height"`
   212  	HashRate    uint64   `json:"hash_rate"`
   213  }
   214  
   215  func (a *API) getHashRate(ins BlockReq) Response {
   216  	if len(ins.BlockHash) != 32 && len(ins.BlockHash) != 0 {
   217  		err := errors.New("Block hash format error.")
   218  		return NewErrorResponse(err)
   219  	}
   220  	if ins.BlockHeight == 0 {
   221  		ins.BlockHeight = a.chain.BestBlockHeight()
   222  	}
   223  
   224  	block, err := a.getBlockHelper(ins)
   225  	if err != nil {
   226  		return NewErrorResponse(err)
   227  	}
   228  
   229  	preBlock, err := a.chain.GetBlockByHash(&block.PreviousBlockHash)
   230  	if err != nil {
   231  		return NewErrorResponse(err)
   232  	}
   233  
   234  	diffTime := block.Timestamp - preBlock.Timestamp
   235  	if preBlock.Timestamp >= block.Timestamp {
   236  		diffTime = 1
   237  	}
   238  	hashCount := difficulty.CalcWork(block.Bits)
   239  	hashRate := new(big.Int).Div(hashCount, big.NewInt(int64(diffTime)))
   240  
   241  	blockHash := block.Hash()
   242  	resp := &getHashRateResp{
   243  		BlockHash:   &blockHash,
   244  		BlockHeight: block.Height,
   245  		HashRate:    hashRate.Uint64(),
   246  	}
   247  	return NewSuccessResponse(resp)
   248  }
   249  
   250  // MerkleBlockReq is used to handle getTxOutProof req
   251  type MerkleBlockReq struct {
   252  	TxIDs     []chainjson.HexBytes `json:"tx_ids"`
   253  	BlockHash chainjson.HexBytes   `json:"block_hash"`
   254  }
   255  
   256  // GetMerkleBlockResp is resp struct for GetTxOutProof API
   257  type GetMerkleBlockResp struct {
   258  	BlockHeader  types.BlockHeader `json:"block_header"`
   259  	TxHashes     []*bc.Hash        `json:"tx_hashes"`
   260  	StatusHashes []*bc.Hash        `json:"status_hashes"`
   261  	Flags        []uint32          `json:"flags"`
   262  	MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
   263  }
   264  
   265  func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
   266  	blockReq := BlockReq{BlockHash: ins.BlockHash}
   267  	block, err := a.getBlockHelper(blockReq)
   268  	if err != nil {
   269  		return NewErrorResponse(err)
   270  	}
   271  
   272  	matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
   273  	var matchedTxIDs []*bc.Hash
   274  	for _, tx := range matchedTxs {
   275  		matchedTxIDs = append(matchedTxIDs, &tx.ID)
   276  	}
   277  
   278  	hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
   279  	flags := make([]uint32, len(compactFlags))
   280  	for i, flag := range compactFlags {
   281  		flags[i] = uint32(flag)
   282  	}
   283  
   284  	blockHash := block.Hash()
   285  	statuses, err := a.chain.GetTransactionStatus(&blockHash)
   286  	if err != nil {
   287  		return NewErrorResponse(err)
   288  	}
   289  
   290  	statusHashes := types.GetStatusMerkleTreeProof(statuses.VerifyStatus, compactFlags)
   291  
   292  	resp := &GetMerkleBlockResp{
   293  		BlockHeader:  block.BlockHeader,
   294  		TxHashes:     hashes,
   295  		StatusHashes: statusHashes,
   296  		Flags:        flags,
   297  		MatchedTxIDs: matchedTxIDs,
   298  	}
   299  	return NewSuccessResponse(resp)
   300  }
   301  
   302  func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
   303  	txIDSet := set.New()
   304  	for _, txID := range filterTxIDs {
   305  		hash := hexBytesToHash(txID)
   306  		txIDSet.Add(hash.String())
   307  	}
   308  
   309  	var matchedTxs []*types.Tx
   310  	for _, tx := range txs {
   311  		hashStr := tx.ID.String()
   312  		if txIDSet.Has(hashStr) {
   313  			matchedTxs = append(matchedTxs, tx)
   314  		}
   315  	}
   316  	return matchedTxs
   317  }