OSDN Git Service

refactor(bcrp): code refactoring (#1901)
[bytom/bytom.git] / api / block_retrieve.go
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         PreviousBlockHash      *bc.Hash   `json:"previous_block_hash"`
48         Timestamp              uint64     `json:"timestamp"`
49         TransactionsMerkleRoot *bc.Hash   `json:"transaction_merkle_root"`
50         Transactions           []*BlockTx `json:"transactions"`
51 }
52
53 // return block by hash/height
54 func (a *API) getBlock(ins BlockReq) Response {
55         block, err := a.getBlockHelper(ins)
56         if err != nil {
57                 return NewErrorResponse(err)
58         }
59
60         blockHash := block.Hash()
61         rawBlock, err := block.MarshalText()
62         if err != nil {
63                 return NewErrorResponse(err)
64         }
65
66         resp := &GetBlockResp{
67                 Hash:                   &blockHash,
68                 Size:                   uint64(len(rawBlock)),
69                 Version:                block.Version,
70                 Height:                 block.Height,
71                 PreviousBlockHash:      &block.PreviousBlockHash,
72                 Timestamp:              block.Timestamp,
73                 TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
74                 Transactions:           []*BlockTx{},
75         }
76
77         for _, orig := range block.Transactions {
78                 tx := &BlockTx{
79                         ID:        orig.ID,
80                         Version:   orig.Version,
81                         Size:      orig.SerializedSize,
82                         TimeRange: orig.TimeRange,
83                         Inputs:    []*query.AnnotatedInput{},
84                         Outputs:   []*query.AnnotatedOutput{},
85                 }
86
87                 resOutID := orig.ResultIds[0]
88                 resOut, ok := orig.Entries[*resOutID].(*bc.Output)
89                 if ok {
90                         tx.MuxID = *resOut.Source.Ref
91                 } else {
92                         resRetire, _ := orig.Entries[*resOutID].(*bc.Retirement)
93                         tx.MuxID = *resRetire.Source.Ref
94                 }
95
96                 for i := range orig.Inputs {
97                         tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(orig, uint32(i)))
98                 }
99                 for i := range orig.Outputs {
100                         tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(orig, i))
101                 }
102                 resp.Transactions = append(resp.Transactions, tx)
103         }
104         return NewSuccessResponse(resp)
105 }
106
107 // GetRawBlockResp is resp struct for getRawBlock API
108 type GetRawBlockResp struct {
109         RawBlock *types.Block `json:"raw_block"`
110 }
111
112 func (a *API) getRawBlock(ins BlockReq) Response {
113         block, err := a.getBlockHelper(ins)
114         if err != nil {
115                 return NewErrorResponse(err)
116         }
117
118         resp := GetRawBlockResp{
119                 RawBlock: block,
120         }
121         return NewSuccessResponse(resp)
122 }
123
124 // GetBlockHeaderResp is resp struct for getBlockHeader API
125 type GetBlockHeaderResp struct {
126         BlockHeader *types.BlockHeader `json:"block_header"`
127         Reward      uint64             `json:"reward"`
128 }
129
130 func (a *API) getBlockHeader(ins BlockReq) Response {
131         block, err := a.getBlockHelper(ins)
132         if err != nil {
133                 return NewErrorResponse(err)
134         }
135
136         resp := &GetBlockHeaderResp{
137                 BlockHeader: &block.BlockHeader,
138                 Reward:      block.Transactions[0].Outputs[0].Amount,
139         }
140         return NewSuccessResponse(resp)
141 }
142
143 func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
144         if len(ins.BlockHash) == 32 {
145                 hash := hexBytesToHash(ins.BlockHash)
146                 return a.chain.GetBlockByHash(&hash)
147         } else {
148                 return a.chain.GetBlockByHeight(ins.BlockHeight)
149         }
150 }
151
152 func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
153         b32 := [32]byte{}
154         copy(b32[:], hexBytes)
155         return bc.NewHash(b32)
156 }
157
158 // MerkleBlockReq is used to handle getTxOutProof req
159 type MerkleBlockReq struct {
160         TxIDs     []chainjson.HexBytes `json:"tx_ids"`
161         BlockHash chainjson.HexBytes   `json:"block_hash"`
162 }
163
164 // GetMerkleBlockResp is resp struct for GetTxOutProof API
165 type GetMerkleBlockResp struct {
166         BlockHeader  types.BlockHeader `json:"block_header"`
167         TxHashes     []*bc.Hash        `json:"tx_hashes"`
168         Flags        []uint32          `json:"flags"`
169         MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
170 }
171
172 func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
173         blockReq := BlockReq{BlockHash: ins.BlockHash}
174         block, err := a.getBlockHelper(blockReq)
175         if err != nil {
176                 return NewErrorResponse(err)
177         }
178
179         matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
180         var matchedTxIDs []*bc.Hash
181         for _, tx := range matchedTxs {
182                 matchedTxIDs = append(matchedTxIDs, &tx.ID)
183         }
184
185         hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
186         flags := make([]uint32, len(compactFlags))
187         for i, flag := range compactFlags {
188                 flags[i] = uint32(flag)
189         }
190
191         resp := &GetMerkleBlockResp{
192                 BlockHeader:  block.BlockHeader,
193                 TxHashes:     hashes,
194                 Flags:        flags,
195                 MatchedTxIDs: matchedTxIDs,
196         }
197         return NewSuccessResponse(resp)
198 }
199
200 func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
201         txIDSet := set.New(set.ThreadSafe)
202         for _, txID := range filterTxIDs {
203                 hash := hexBytesToHash(txID)
204                 txIDSet.Add(hash.String())
205         }
206
207         var matchedTxs []*types.Tx
208         for _, tx := range txs {
209                 hashStr := tx.ID.String()
210                 if txIDSet.Has(hashStr) {
211                         matchedTxs = append(matchedTxs, tx)
212                 }
213         }
214         return matchedTxs
215 }