OSDN Git Service

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