OSDN Git Service

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