OSDN Git Service

Hulk did something
[bytom/vapor.git] / api / block_retrieve.go
diff --git a/api/block_retrieve.go b/api/block_retrieve.go
new file mode 100644 (file)
index 0000000..ed04369
--- /dev/null
@@ -0,0 +1,317 @@
+package api
+
+import (
+       "math/big"
+
+       "gopkg.in/fatih/set.v0"
+
+       "github.com/vapor/blockchain/query"
+       "github.com/vapor/consensus/difficulty"
+       chainjson "github.com/vapor/encoding/json"
+       "github.com/vapor/errors"
+       "github.com/vapor/protocol/bc"
+       "github.com/vapor/protocol/bc/types"
+)
+
+// return best block hash
+func (a *API) getBestBlockHash() Response {
+       blockHash := map[string]string{"block_hash": a.chain.BestBlockHash().String()}
+       return NewSuccessResponse(blockHash)
+}
+
+// return current block count
+func (a *API) getBlockCount() Response {
+       blockHeight := map[string]uint64{"block_count": a.chain.BestBlockHeight()}
+       return NewSuccessResponse(blockHeight)
+}
+
+// BlockTx is the tx struct for getBlock func
+type BlockTx struct {
+       ID         bc.Hash                  `json:"id"`
+       Version    uint64                   `json:"version"`
+       Size       uint64                   `json:"size"`
+       TimeRange  uint64                   `json:"time_range"`
+       Inputs     []*query.AnnotatedInput  `json:"inputs"`
+       Outputs    []*query.AnnotatedOutput `json:"outputs"`
+       StatusFail bool                     `json:"status_fail"`
+       MuxID      bc.Hash                  `json:"mux_id"`
+}
+
+// BlockReq is used to handle getBlock req
+type BlockReq struct {
+       BlockHeight uint64             `json:"block_height"`
+       BlockHash   chainjson.HexBytes `json:"block_hash"`
+}
+
+// GetBlockResp is the resp for getBlock api
+type GetBlockResp struct {
+       Hash                   *bc.Hash   `json:"hash"`
+       Size                   uint64     `json:"size"`
+       Version                uint64     `json:"version"`
+       Height                 uint64     `json:"height"`
+       PreviousBlockHash      *bc.Hash   `json:"previous_block_hash"`
+       Timestamp              uint64     `json:"timestamp"`
+       Nonce                  uint64     `json:"nonce"`
+       Bits                   uint64     `json:"bits"`
+       Difficulty             string     `json:"difficulty"`
+       TransactionsMerkleRoot *bc.Hash   `json:"transaction_merkle_root"`
+       TransactionStatusHash  *bc.Hash   `json:"transaction_status_hash"`
+       Transactions           []*BlockTx `json:"transactions"`
+}
+
+// return block by hash/height
+func (a *API) getBlock(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       blockHash := block.Hash()
+       txStatus, err := a.chain.GetTransactionStatus(&blockHash)
+       rawBlock, err := block.MarshalText()
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       resp := &GetBlockResp{
+               Hash:                   &blockHash,
+               Size:                   uint64(len(rawBlock)),
+               Version:                block.Version,
+               Height:                 block.Height,
+               PreviousBlockHash:      &block.PreviousBlockHash,
+               Timestamp:              block.Timestamp,
+               Nonce:                  block.Nonce,
+               Bits:                   block.Bits,
+               Difficulty:             difficulty.CalcWork(block.Bits).String(),
+               TransactionsMerkleRoot: &block.TransactionsMerkleRoot,
+               TransactionStatusHash:  &block.TransactionStatusHash,
+               Transactions:           []*BlockTx{},
+       }
+
+       for i, orig := range block.Transactions {
+               tx := &BlockTx{
+                       ID:        orig.ID,
+                       Version:   orig.Version,
+                       Size:      orig.SerializedSize,
+                       TimeRange: orig.TimeRange,
+                       Inputs:    []*query.AnnotatedInput{},
+                       Outputs:   []*query.AnnotatedOutput{},
+               }
+               tx.StatusFail, err = txStatus.GetStatus(i)
+               if err != nil {
+                       return NewSuccessResponse(resp)
+               }
+
+               resOutID := orig.ResultIds[0]
+               resOut, ok := orig.Entries[*resOutID].(*bc.Output)
+               if ok {
+                       tx.MuxID = *resOut.Source.Ref
+               } else {
+                       resRetire, _ := orig.Entries[*resOutID].(*bc.Retirement)
+                       tx.MuxID = *resRetire.Source.Ref
+               }
+
+               for i := range orig.Inputs {
+                       tx.Inputs = append(tx.Inputs, a.wallet.BuildAnnotatedInput(orig, uint32(i)))
+               }
+               for i := range orig.Outputs {
+                       tx.Outputs = append(tx.Outputs, a.wallet.BuildAnnotatedOutput(orig, i))
+               }
+               resp.Transactions = append(resp.Transactions, tx)
+       }
+       return NewSuccessResponse(resp)
+}
+
+// GetRawBlockResp is resp struct for getRawBlock API
+type GetRawBlockResp struct {
+       RawBlock          *types.Block          `json:"raw_block"`
+       TransactionStatus *bc.TransactionStatus `json:"transaction_status"`
+}
+
+func (a *API) getRawBlock(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       blockHash := block.Hash()
+       txStatus, err := a.chain.GetTransactionStatus(&blockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       resp := GetRawBlockResp{
+               RawBlock:          block,
+               TransactionStatus: txStatus,
+       }
+       return NewSuccessResponse(resp)
+}
+
+// GetBlockHeaderResp is resp struct for getBlockHeader API
+type GetBlockHeaderResp struct {
+       BlockHeader *types.BlockHeader `json:"block_header"`
+       Reward      uint64             `json:"reward"`
+}
+
+func (a *API) getBlockHeader(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       resp := &GetBlockHeaderResp{
+               BlockHeader: &block.BlockHeader,
+               Reward:      block.Transactions[0].Outputs[0].Amount,
+       }
+       return NewSuccessResponse(resp)
+}
+
+func (a *API) getBlockHelper(ins BlockReq) (*types.Block, error) {
+       if len(ins.BlockHash) == 32 {
+               hash := hexBytesToHash(ins.BlockHash)
+               return a.chain.GetBlockByHash(&hash)
+       } else {
+               return a.chain.GetBlockByHeight(ins.BlockHeight)
+       }
+}
+
+func hexBytesToHash(hexBytes chainjson.HexBytes) bc.Hash {
+       b32 := [32]byte{}
+       copy(b32[:], hexBytes)
+       return bc.NewHash(b32)
+}
+
+// GetDifficultyResp is resp struct for getDifficulty API
+type GetDifficultyResp struct {
+       BlockHash   *bc.Hash `json:"hash"`
+       BlockHeight uint64   `json:"height"`
+       Bits        uint64   `json:"bits"`
+       Difficulty  string   `json:"difficulty"`
+}
+
+func (a *API) getDifficulty(ins BlockReq) Response {
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       blockHash := block.Hash()
+       resp := &GetDifficultyResp{
+               BlockHash:   &blockHash,
+               BlockHeight: block.Height,
+               Bits:        block.Bits,
+               Difficulty:  difficulty.CalcWork(block.Bits).String(),
+       }
+       return NewSuccessResponse(resp)
+}
+
+// getHashRateResp is resp struct for getHashRate API
+type getHashRateResp struct {
+       BlockHash   *bc.Hash `json:"hash"`
+       BlockHeight uint64   `json:"height"`
+       HashRate    uint64   `json:"hash_rate"`
+}
+
+func (a *API) getHashRate(ins BlockReq) Response {
+       if len(ins.BlockHash) != 32 && len(ins.BlockHash) != 0 {
+               err := errors.New("Block hash format error.")
+               return NewErrorResponse(err)
+       }
+       if ins.BlockHeight == 0 {
+               ins.BlockHeight = a.chain.BestBlockHeight()
+       }
+
+       block, err := a.getBlockHelper(ins)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       preBlock, err := a.chain.GetBlockByHash(&block.PreviousBlockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       diffTime := block.Timestamp - preBlock.Timestamp
+       if preBlock.Timestamp >= block.Timestamp {
+               diffTime = 1
+       }
+       hashCount := difficulty.CalcWork(block.Bits)
+       hashRate := new(big.Int).Div(hashCount, big.NewInt(int64(diffTime)))
+
+       blockHash := block.Hash()
+       resp := &getHashRateResp{
+               BlockHash:   &blockHash,
+               BlockHeight: block.Height,
+               HashRate:    hashRate.Uint64(),
+       }
+       return NewSuccessResponse(resp)
+}
+
+// MerkleBlockReq is used to handle getTxOutProof req
+type MerkleBlockReq struct {
+       TxIDs     []chainjson.HexBytes `json:"tx_ids"`
+       BlockHash chainjson.HexBytes   `json:"block_hash"`
+}
+
+// GetMerkleBlockResp is resp struct for GetTxOutProof API
+type GetMerkleBlockResp struct {
+       BlockHeader  types.BlockHeader `json:"block_header"`
+       TxHashes     []*bc.Hash        `json:"tx_hashes"`
+       StatusHashes []*bc.Hash        `json:"status_hashes"`
+       Flags        []uint32          `json:"flags"`
+       MatchedTxIDs []*bc.Hash        `json:"matched_tx_ids"`
+}
+
+func (a *API) getMerkleProof(ins MerkleBlockReq) Response {
+       blockReq := BlockReq{BlockHash: ins.BlockHash}
+       block, err := a.getBlockHelper(blockReq)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       matchedTxs := getMatchedTx(block.Transactions, ins.TxIDs)
+       var matchedTxIDs []*bc.Hash
+       for _, tx := range matchedTxs {
+               matchedTxIDs = append(matchedTxIDs, &tx.ID)
+       }
+
+       hashes, compactFlags := types.GetTxMerkleTreeProof(block.Transactions, matchedTxs)
+       flags := make([]uint32, len(compactFlags))
+       for i, flag := range compactFlags {
+               flags[i] = uint32(flag)
+       }
+
+       blockHash := block.Hash()
+       statuses, err := a.chain.GetTransactionStatus(&blockHash)
+       if err != nil {
+               return NewErrorResponse(err)
+       }
+
+       statusHashes := types.GetStatusMerkleTreeProof(statuses.VerifyStatus, compactFlags)
+
+       resp := &GetMerkleBlockResp{
+               BlockHeader:  block.BlockHeader,
+               TxHashes:     hashes,
+               StatusHashes: statusHashes,
+               Flags:        flags,
+               MatchedTxIDs: matchedTxIDs,
+       }
+       return NewSuccessResponse(resp)
+}
+
+func getMatchedTx(txs []*types.Tx, filterTxIDs []chainjson.HexBytes) []*types.Tx {
+       txIDSet := set.New()
+       for _, txID := range filterTxIDs {
+               hash := hexBytesToHash(txID)
+               txIDSet.Add(hash.String())
+       }
+
+       var matchedTxs []*types.Tx
+       for _, tx := range txs {
+               hashStr := tx.ID.String()
+               if txIDSet.Has(hashStr) {
+                       matchedTxs = append(matchedTxs, tx)
+               }
+       }
+       return matchedTxs
+}