OSDN Git Service

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