github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/types/block.go (about)

     1  package types
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"math/big"
     9  	"strings"
    10  
    11  	"github.com/ethereum/go-ethereum/common"
    12  	"github.com/ethereum/go-ethereum/common/hexutil"
    13  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    14  )
    15  
    16  // BlockNumber represents decoding hex string to block values
    17  type BlockNumber int64
    18  
    19  var (
    20  	// LatestBlockNumber mapping from "latest" to 0 for tm query
    21  	LatestBlockNumber = BlockNumber(0)
    22  	// PendingBlockNumber mapping from "pending" to -1 for tm query
    23  	PendingBlockNumber = BlockNumber(-1)
    24  	// EarliestBlockNumber mapping from "earliest" to (genesisHeight + 1) for tm query (earliest query not supported)
    25  	EarliestBlockNumber = BlockNumber(tmtypes.GetStartBlockHeight() + 1)
    26  
    27  	ErrResourceNotFound = errors.New("resource not found")
    28  )
    29  
    30  // NewBlockNumber creates a new BlockNumber instance.
    31  func NewBlockNumber(n *big.Int) BlockNumber {
    32  	return BlockNumber(n.Int64())
    33  }
    34  
    35  // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
    36  // - "latest", "earliest" or "pending" as string arguments
    37  // - the block number
    38  // Returned errors:
    39  // - an invalid block number error when the given argument isn't a known strings
    40  // - an out of range error when the given block number is either too little or too large
    41  func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
    42  	input := strings.TrimSpace(string(data))
    43  	if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
    44  		input = input[1 : len(input)-1]
    45  	}
    46  
    47  	switch input {
    48  	case "earliest":
    49  		*bn = EarliestBlockNumber
    50  		return nil
    51  	case "latest":
    52  		*bn = LatestBlockNumber
    53  		return nil
    54  	case "pending":
    55  		*bn = PendingBlockNumber
    56  		return nil
    57  	}
    58  
    59  	blckNum, err := hexutil.DecodeUint64(input)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	if blckNum > math.MaxInt64 {
    64  		return fmt.Errorf("blocknumber too high")
    65  	}
    66  
    67  	*bn = BlockNumber(blckNum)
    68  	return nil
    69  }
    70  
    71  // Int64 converts block number to primitive type
    72  func (bn BlockNumber) Int64() int64 {
    73  	return int64(bn)
    74  }
    75  
    76  // TmHeight is a util function used for the Tendermint RPC client. It returns
    77  // nil if the block number is "latest". Otherwise, it returns the pointer of the
    78  // int64 value of the height.
    79  func (bn BlockNumber) TmHeight() *int64 {
    80  	if bn == LatestBlockNumber {
    81  		return nil
    82  	}
    83  	height := bn.Int64()
    84  	return &height
    85  }
    86  
    87  type BlockNumberOrHash struct {
    88  	BlockNumber      *BlockNumber `json:"blockNumber,omitempty"`
    89  	BlockHash        *common.Hash `json:"blockHash,omitempty"`
    90  	RequireCanonical bool         `json:"requireCanonical,omitempty"`
    91  }
    92  
    93  func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
    94  	type erased BlockNumberOrHash
    95  	e := erased{}
    96  	err := json.Unmarshal(data, &e)
    97  	if err == nil {
    98  		if e.BlockNumber != nil && e.BlockHash != nil {
    99  			return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
   100  		}
   101  		bnh.BlockNumber = e.BlockNumber
   102  		bnh.BlockHash = e.BlockHash
   103  		bnh.RequireCanonical = e.RequireCanonical
   104  		return nil
   105  	}
   106  	var input string
   107  	err = json.Unmarshal(data, &input)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	switch input {
   112  	case "earliest":
   113  		bn := EarliestBlockNumber
   114  		bnh.BlockNumber = &bn
   115  		return nil
   116  	case "latest":
   117  		bn := LatestBlockNumber
   118  		bnh.BlockNumber = &bn
   119  		return nil
   120  	case "pending":
   121  		bn := PendingBlockNumber
   122  		bnh.BlockNumber = &bn
   123  		return nil
   124  	default:
   125  		if len(input) == 66 {
   126  			hash := common.Hash{}
   127  			err := hash.UnmarshalText([]byte(input))
   128  			if err != nil {
   129  				return err
   130  			}
   131  			bnh.BlockHash = &hash
   132  			return nil
   133  		} else {
   134  			blckNum, err := hexutil.DecodeUint64(input)
   135  			if err != nil {
   136  				return err
   137  			}
   138  			if blckNum > math.MaxInt64 {
   139  				return fmt.Errorf("blocknumber too high")
   140  			}
   141  			bn := BlockNumber(blckNum)
   142  			bnh.BlockNumber = &bn
   143  			return nil
   144  		}
   145  	}
   146  }
   147  
   148  func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
   149  	if bnh.BlockNumber != nil {
   150  		return *bnh.BlockNumber, true
   151  	}
   152  	return BlockNumber(0), false
   153  }
   154  
   155  func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) {
   156  	if bnh.BlockHash != nil {
   157  		return *bnh.BlockHash, true
   158  	}
   159  	return common.Hash{}, false
   160  }
   161  
   162  func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash {
   163  	return BlockNumberOrHash{
   164  		BlockNumber:      &blockNr,
   165  		BlockHash:        nil,
   166  		RequireCanonical: false,
   167  	}
   168  }
   169  
   170  func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash {
   171  	return BlockNumberOrHash{
   172  		BlockNumber:      nil,
   173  		BlockHash:        &hash,
   174  		RequireCanonical: canonical,
   175  	}
   176  }