OSDN Git Service

ed043695696bcc5b2ce14107453e4e38aad214d5
[bytom/vapor.git] / api / block_retrieve.go
1 package api
2
3 import (
4         "math/big"
5
6         "gopkg.in/fatih/set.v0"
7
8         "github.com/vapor/blockchain/query"
9         "github.com/vapor/consensus/difficulty"
10         chainjson "github.com/vapor/encoding/json"
11         "github.com/vapor/errors"
12         "github.com/vapor/protocol/bc"
13         "github.com/vapor/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 }