OSDN Git Service

fix bug (#260)
[bytom/vapor.git] / api / block_retrieve.go
1 package api
2
3 import (
4         set "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         Witness                []chainjson.HexBytes `json:"witness"`
51         Blocker                string               `json:"blocker"`
52         TransactionsMerkleRoot *bc.Hash             `json:"transaction_merkle_root"`
53         TransactionStatusHash  *bc.Hash             `json:"transaction_status_hash"`
54         Transactions           []*BlockTx           `json:"transactions"`
55 }
56
57 // return block by hash/height
58 func (a *API) getBlock(ins BlockReq) Response {
59         block, err := a.getBlockHelper(ins)
60         if err != nil {
61                 return NewErrorResponse(err)
62         }
63
64         blockHash := block.Hash()
65         txStatus, err := a.chain.GetTransactionStatus(&blockHash)
66         rawBlock, err := block.MarshalText()
67         if err != nil {
68                 return NewErrorResponse(err)
69         }
70
71         witness := make([]chainjson.HexBytes, len(block.Witness))
72         for i, w := range block.Witness {
73                 witness[i] = w
74         }
75
76         blocker, err := a.chain.GetBlocker(&block.PreviousBlockHash, block.Timestamp)
77         if err != nil {
78                 return NewErrorResponse(err)
79         }
80
81         resp := &GetBlockResp{
82                 Hash:                   &blockHash,
83                 Size:                   uint64(len(rawBlock)),
84                 Version:                block.Version,
85                 Height:                 block.Height,
86                 PreviousBlockHash:      &block.PreviousBlockHash,
87                 Timestamp:              block.Timestamp,
88                 Witness:                witness,
89                 Blocker:                blocker,
90                 TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
91                 TransactionStatusHash:  &block.TransactionStatusHash,
92                 Transactions:           []*BlockTx{},
93         }
94
95         for i, orig := range block.Transactions {
96                 tx := &BlockTx{
97                         ID:        orig.ID,
98                         Version:   orig.Version,
99                         Size:      orig.SerializedSize,
100                         TimeRange: orig.TimeRange,
101                         Inputs:    []*query.AnnotatedInput{},
102                         Outputs:   []*query.AnnotatedOutput{},
103                 }
104                 tx.StatusFail, err = txStatus.GetStatus(i)
105                 if err != nil {
106                         return NewSuccessResponse(resp)
107                 }
108
109                 resOutID := orig.ResultIds[0]
110                 switch resOut := orig.Entries[*resOutID].(type) {
111                 case *bc.IntraChainOutput:
112                         tx.MuxID = *resOut.Source.Ref
113                 case *bc.CrossChainOutput:
114                         tx.MuxID = *resOut.Source.Ref
115                 case *bc.VoteOutput:
116                         tx.MuxID = *resOut.Source.Ref
117                 case *bc.Retirement:
118                         resRetire, _ := orig.Entries[*resOutID].(*bc.Retirement)
119                         tx.MuxID = *resRetire.Source.Ref
120                 }
121
122                 for i := range orig.Inputs {
123                         tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(orig, uint32(i)))
124                 }
125                 for i := range orig.Outputs {
126                         tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(orig, i))
127                 }
128                 resp.Transactions = append(resp.Transactions, tx)
129         }
130         return NewSuccessResponse(resp)
131 }
132
133 // GetRawBlockResp is resp struct for getRawBlock API
134 type GetRawBlockResp struct {
135         RawBlock          *types.Block          `json:"raw_block"`
136         TransactionStatus *bc.TransactionStatus `json:"transaction_status"`
137 }
138
139 func (a *API) getRawBlock(ins BlockReq) Response {
140         block, err := a.getBlockHelper(ins)
141         if err != nil {
142                 return NewErrorResponse(err)
143         }
144
145         blockHash := block.Hash()
146         txStatus, err := a.chain.GetTransactionStatus(&blockHash)
147         if err != nil {
148                 return NewErrorResponse(err)
149         }
150
151         resp := GetRawBlockResp{
152                 RawBlock:          block,
153                 TransactionStatus: txStatus,
154         }
155         return NewSuccessResponse(resp)
156 }
157
158 // GetBlockHeaderResp is resp struct for getBlockHeader API
159 type GetBlockHeaderResp struct {
160         BlockHeader *types.BlockHeader `json:"block_header"`
161         Reward      uint64             `json:"reward"`
162 }
163
164 func (a *API) getBlockHeader(ins BlockReq) Response {
165         block, err := a.getBlockHelper(ins)
166         if err != nil {
167                 return NewErrorResponse(err)
168         }
169
170         resp := &GetBlockHeaderResp{
171                 BlockHeader: &block.BlockHeader,
172                 Reward:      block.Transactions[0].Outputs[0].AssetAmount().Amount,
173         }
174         return NewSuccessResponse(resp)
175 }
176
177 func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
178         if len(ins.BlockHash) == 32 {
179                 hash := hexBytesToHash(ins.BlockHash)
180                 return a.chain.GetBlockByHash(&hash)
181         } else {
182                 return a.chain.GetBlockByHeight(ins.BlockHeight)
183         }
184 }
185
186 func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
187         b32 := [32]byte{}
188         copy(b32[:], hexBytes)
189         return bc.NewHash(b32)
190 }
191
192 // MerkleBlockReq is used to handle getTxOutProof req
193 type MerkleBlockReq struct {
194         TxIDs     []chainjson.HexBytes `json:"tx_ids"`
195         BlockHash chainjson.HexBytes   `json:"block_hash"`
196 }
197
198 // GetMerkleBlockResp is resp struct for GetTxOutProof API
199 type GetMerkleBlockResp struct {
200         BlockHeader  types.BlockHeader `json:"block_header"`
201         TxHashes     []*bc.Hash        `json:"tx_hashes"`
202         StatusHashes []*bc.Hash        `json:"status_hashes"`
203         Flags        []uint32          `json:"flags"`
204         MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
205 }
206
207 func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
208         blockReq := BlockReq{BlockHash: ins.BlockHash}
209         block, err := a.getBlockHelper(blockReq)
210         if err != nil {
211                 return NewErrorResponse(err)
212         }
213
214         matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
215         var matchedTxIDs []*bc.Hash
216         for _, tx := range matchedTxs {
217                 matchedTxIDs = append(matchedTxIDs, &tx.ID)
218         }
219
220         hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
221         flags := make([]uint32, len(compactFlags))
222         for i, flag := range compactFlags {
223                 flags[i] = uint32(flag)
224         }
225
226         blockHash := block.Hash()
227         statuses, err := a.chain.GetTransactionStatus(&blockHash)
228         if err != nil {
229                 return NewErrorResponse(err)
230         }
231
232         statusHashes := types.GetStatusMerkleTreeProof(statuses.VerifyStatus, compactFlags)
233
234         resp := &GetMerkleBlockResp{
235                 BlockHeader:  block.BlockHeader,
236                 TxHashes:     hashes,
237                 StatusHashes: statusHashes,
238                 Flags:        flags,
239                 MatchedTxIDs: matchedTxIDs,
240         }
241         return NewSuccessResponse(resp)
242 }
243
244 func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
245         txIDSet := set.New()
246         for _, txID := range filterTxIDs {
247                 hash := hexBytesToHash(txID)
248                 txIDSet.Add(hash.String())
249         }
250
251         var matchedTxs []*types.Tx
252         for _, tx := range txs {
253                 hashStr := tx.ID.String()
254                 if txIDSet.Has(hashStr) {
255                         matchedTxs = append(matchedTxs, tx)
256                 }
257         }
258         return matchedTxs
259 }