OSDN Git Service

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