github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/rpc/types.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package rpc
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"math"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/hardtosaygoodbye/go-ethereum/common"
    28  	"github.com/hardtosaygoodbye/go-ethereum/common/hexutil"
    29  )
    30  
    31  // API describes the set of methods offered over the RPC interface
    32  type API struct {
    33  	Namespace string      // namespace under which the rpc methods of Service are exposed
    34  	Version   string      // api version for DApp's
    35  	Service   interface{} // receiver instance which holds the methods
    36  	Public    bool        // indication if the methods must be considered safe for public use
    37  }
    38  
    39  // ServerCodec implements reading, parsing and writing RPC messages for the server side of
    40  // a RPC session. Implementations must be go-routine safe since the codec can be called in
    41  // multiple go-routines concurrently.
    42  type ServerCodec interface {
    43  	peerInfo() PeerInfo
    44  	readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
    45  	close()
    46  
    47  	jsonWriter
    48  }
    49  
    50  // jsonWriter can write JSON messages to its underlying connection.
    51  // Implementations must be safe for concurrent use.
    52  type jsonWriter interface {
    53  	writeJSON(context.Context, interface{}) error
    54  	// Closed returns a channel which is closed when the connection is closed.
    55  	closed() <-chan interface{}
    56  	// RemoteAddr returns the peer address of the connection.
    57  	remoteAddr() string
    58  }
    59  
    60  type BlockNumber int64
    61  
    62  const (
    63  	PendingBlockNumber  = BlockNumber(-2)
    64  	LatestBlockNumber   = BlockNumber(-1)
    65  	EarliestBlockNumber = BlockNumber(0)
    66  )
    67  
    68  // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
    69  // - "latest", "earliest" or "pending" as string arguments
    70  // - the block number
    71  // Returned errors:
    72  // - an invalid block number error when the given argument isn't a known strings
    73  // - an out of range error when the given block number is either too little or too large
    74  func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
    75  	input := strings.TrimSpace(string(data))
    76  	if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
    77  		input = input[1 : len(input)-1]
    78  	}
    79  
    80  	switch input {
    81  	case "earliest":
    82  		*bn = EarliestBlockNumber
    83  		return nil
    84  	case "latest":
    85  		*bn = LatestBlockNumber
    86  		return nil
    87  	case "pending":
    88  		*bn = PendingBlockNumber
    89  		return nil
    90  	}
    91  
    92  	blckNum, err := hexutil.DecodeUint64(input)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	if blckNum > math.MaxInt64 {
    97  		return fmt.Errorf("block number larger than int64")
    98  	}
    99  	*bn = BlockNumber(blckNum)
   100  	return nil
   101  }
   102  
   103  // MarshalText implements encoding.TextMarshaler. It marshals:
   104  // - "latest", "earliest" or "pending" as strings
   105  // - other numbers as hex
   106  func (bn BlockNumber) MarshalText() ([]byte, error) {
   107  	switch bn {
   108  	case EarliestBlockNumber:
   109  		return []byte("earliest"), nil
   110  	case LatestBlockNumber:
   111  		return []byte("latest"), nil
   112  	case PendingBlockNumber:
   113  		return []byte("pending"), nil
   114  	default:
   115  		return hexutil.Uint64(bn).MarshalText()
   116  	}
   117  }
   118  
   119  func (bn BlockNumber) Int64() int64 {
   120  	return (int64)(bn)
   121  }
   122  
   123  type BlockNumberOrHash struct {
   124  	BlockNumber      *BlockNumber `json:"blockNumber,omitempty"`
   125  	BlockHash        *common.Hash `json:"blockHash,omitempty"`
   126  	RequireCanonical bool         `json:"requireCanonical,omitempty"`
   127  }
   128  
   129  func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
   130  	type erased BlockNumberOrHash
   131  	e := erased{}
   132  	err := json.Unmarshal(data, &e)
   133  	if err == nil {
   134  		if e.BlockNumber != nil && e.BlockHash != nil {
   135  			return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
   136  		}
   137  		bnh.BlockNumber = e.BlockNumber
   138  		bnh.BlockHash = e.BlockHash
   139  		bnh.RequireCanonical = e.RequireCanonical
   140  		return nil
   141  	}
   142  	var input string
   143  	err = json.Unmarshal(data, &input)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	switch input {
   148  	case "earliest":
   149  		bn := EarliestBlockNumber
   150  		bnh.BlockNumber = &bn
   151  		return nil
   152  	case "latest":
   153  		bn := LatestBlockNumber
   154  		bnh.BlockNumber = &bn
   155  		return nil
   156  	case "pending":
   157  		bn := PendingBlockNumber
   158  		bnh.BlockNumber = &bn
   159  		return nil
   160  	default:
   161  		if len(input) == 66 {
   162  			hash := common.Hash{}
   163  			err := hash.UnmarshalText([]byte(input))
   164  			if err != nil {
   165  				return err
   166  			}
   167  			bnh.BlockHash = &hash
   168  			return nil
   169  		} else {
   170  			blckNum, err := hexutil.DecodeUint64(input)
   171  			if err != nil {
   172  				return err
   173  			}
   174  			if blckNum > math.MaxInt64 {
   175  				return fmt.Errorf("blocknumber too high")
   176  			}
   177  			bn := BlockNumber(blckNum)
   178  			bnh.BlockNumber = &bn
   179  			return nil
   180  		}
   181  	}
   182  }
   183  
   184  func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
   185  	if bnh.BlockNumber != nil {
   186  		return *bnh.BlockNumber, true
   187  	}
   188  	return BlockNumber(0), false
   189  }
   190  
   191  func (bnh *BlockNumberOrHash) String() string {
   192  	if bnh.BlockNumber != nil {
   193  		return strconv.Itoa(int(*bnh.BlockNumber))
   194  	}
   195  	if bnh.BlockHash != nil {
   196  		return bnh.BlockHash.String()
   197  	}
   198  	return "nil"
   199  }
   200  
   201  func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) {
   202  	if bnh.BlockHash != nil {
   203  		return *bnh.BlockHash, true
   204  	}
   205  	return common.Hash{}, false
   206  }
   207  
   208  func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash {
   209  	return BlockNumberOrHash{
   210  		BlockNumber:      &blockNr,
   211  		BlockHash:        nil,
   212  		RequireCanonical: false,
   213  	}
   214  }
   215  
   216  func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash {
   217  	return BlockNumberOrHash{
   218  		BlockNumber:      nil,
   219  		BlockHash:        &hash,
   220  		RequireCanonical: canonical,
   221  	}
   222  }
   223  
   224  // DecimalOrHex unmarshals a non-negative decimal or hex parameter into a uint64.
   225  type DecimalOrHex uint64
   226  
   227  // UnmarshalJSON implements json.Unmarshaler.
   228  func (dh *DecimalOrHex) UnmarshalJSON(data []byte) error {
   229  	input := strings.TrimSpace(string(data))
   230  	if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
   231  		input = input[1 : len(input)-1]
   232  	}
   233  
   234  	value, err := strconv.ParseUint(input, 10, 64)
   235  	if err != nil {
   236  		value, err = hexutil.DecodeUint64(input)
   237  	}
   238  	if err != nil {
   239  		return err
   240  	}
   241  	*dh = DecimalOrHex(value)
   242  	return nil
   243  }