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 }