github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/backend/api.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from quorum/consensus/istanbul/backend/api.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package backend
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"math/big"
    27  	"reflect"
    28  
    29  	klaytnApi "github.com/klaytn/klaytn/api"
    30  	"github.com/klaytn/klaytn/blockchain"
    31  	"github.com/klaytn/klaytn/blockchain/types"
    32  	"github.com/klaytn/klaytn/common"
    33  	"github.com/klaytn/klaytn/consensus"
    34  	"github.com/klaytn/klaytn/consensus/istanbul"
    35  	"github.com/klaytn/klaytn/networks/rpc"
    36  )
    37  
    38  // API is a user facing RPC API to dump Istanbul state
    39  type API struct {
    40  	chain    consensus.ChainReader
    41  	istanbul *backend
    42  }
    43  
    44  // GetSnapshot retrieves the state snapshot at a given block.
    45  func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
    46  	// Retrieve the requested block number (or current if none requested)
    47  	header, err := headerByRpcNumber(api.chain, number)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false)
    53  }
    54  
    55  // GetSnapshotAtHash retrieves the state snapshot at a given block.
    56  func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
    57  	header := api.chain.GetHeaderByHash(hash)
    58  	if header == nil {
    59  		return nil, errUnknownBlock
    60  	}
    61  	return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false)
    62  }
    63  
    64  // GetValidators retrieves the list of authorized validators with the given block number.
    65  func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) {
    66  	header, err := headerByRpcNumber(api.chain, number)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	blockNumber := header.Number.Uint64()
    72  	if blockNumber == 0 {
    73  		// The committee of genesis block can not be calculated because it requires a previous block.
    74  		istanbulExtra, err := types.ExtractIstanbulExtra(header)
    75  		if err != nil {
    76  			return nil, errExtractIstanbulExtra
    77  		}
    78  		return istanbulExtra.Validators, nil
    79  	}
    80  
    81  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
    82  	if err != nil {
    83  		logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
    84  		return nil, err
    85  	}
    86  	return snap.validators(), nil
    87  }
    88  
    89  // GetValidatorsAtHash retrieves the list of authorized validators with the given block hash.
    90  func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error) {
    91  	header := api.chain.GetHeaderByHash(hash)
    92  	if header == nil {
    93  		return nil, errUnknownBlock
    94  	}
    95  
    96  	blockNumber := header.Number.Uint64()
    97  	if blockNumber == 0 {
    98  		// The committee of genesis block can not be calculated because it requires a previous block.
    99  		istanbulExtra, err := types.ExtractIstanbulExtra(header)
   100  		if err != nil {
   101  			return nil, errExtractIstanbulExtra
   102  		}
   103  		return istanbulExtra.Validators, nil
   104  	}
   105  
   106  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
   107  	if err != nil {
   108  		logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   109  		return nil, errInternalError
   110  	}
   111  	return snap.validators(), nil
   112  }
   113  
   114  // GetDemotedValidators retrieves the list of authorized, but demoted validators with the given block number.
   115  func (api *API) GetDemoteValidators(number *rpc.BlockNumber) ([]common.Address, error) {
   116  	header, err := headerByRpcNumber(api.chain, number)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	blockNumber := header.Number.Uint64()
   122  	if blockNumber == 0 {
   123  		snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false)
   124  		if err != nil {
   125  			logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   126  			return nil, err
   127  		}
   128  		return snap.demotedValidators(), nil
   129  	} else {
   130  		snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
   131  		if err != nil {
   132  			logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   133  			return nil, err
   134  		}
   135  		return snap.demotedValidators(), nil
   136  	}
   137  }
   138  
   139  // GetDemotedValidatorsAtHash retrieves the list of authorized, but demoted validators with the given block hash.
   140  func (api *API) GetDemotedValidatorsAtHash(hash common.Hash) ([]common.Address, error) {
   141  	header := api.chain.GetHeaderByHash(hash)
   142  	if header == nil {
   143  		return nil, errUnknownBlock
   144  	}
   145  
   146  	blockNumber := header.Number.Uint64()
   147  	if blockNumber == 0 {
   148  		snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil, false)
   149  		if err != nil {
   150  			logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   151  			return nil, err
   152  		}
   153  		return snap.demotedValidators(), nil
   154  	} else {
   155  		snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
   156  		if err != nil {
   157  			logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   158  			return nil, err
   159  		}
   160  		return snap.demotedValidators(), nil
   161  	}
   162  }
   163  
   164  // Candidates returns the current candidates the node tries to uphold and vote on.
   165  func (api *API) Candidates() map[common.Address]bool {
   166  	api.istanbul.candidatesLock.RLock()
   167  	defer api.istanbul.candidatesLock.RUnlock()
   168  
   169  	proposals := make(map[common.Address]bool)
   170  	for address, auth := range api.istanbul.candidates {
   171  		proposals[address] = auth
   172  	}
   173  	return proposals
   174  }
   175  
   176  // Propose injects a new authorization candidate that the validator will attempt to
   177  // push through.
   178  func (api *API) Propose(address common.Address, auth bool) {
   179  	api.istanbul.candidatesLock.Lock()
   180  	defer api.istanbul.candidatesLock.Unlock()
   181  
   182  	api.istanbul.candidates[address] = auth
   183  }
   184  
   185  // Discard drops a currently running candidate, stopping the validator from casting
   186  // further votes (either for or against).
   187  func (api *API) Discard(address common.Address) {
   188  	api.istanbul.candidatesLock.Lock()
   189  	defer api.istanbul.candidatesLock.Unlock()
   190  
   191  	delete(api.istanbul.candidates, address)
   192  }
   193  
   194  // API extended by Klaytn developers
   195  type APIExtension struct {
   196  	chain    consensus.ChainReader
   197  	istanbul *backend
   198  }
   199  
   200  var (
   201  	errPendingNotAllowed       = errors.New("pending is not allowed")
   202  	errInternalError           = errors.New("internal error")
   203  	errStartNotPositive        = errors.New("start block number should be positive")
   204  	errEndLargetThanLatest     = errors.New("end block number should be smaller than the latest block number")
   205  	errStartLargerThanEnd      = errors.New("start should be smaller than end")
   206  	errRequestedBlocksTooLarge = errors.New("number of requested blocks should be smaller than 50")
   207  	errRangeNil                = errors.New("range values should not be nil")
   208  	errExtractIstanbulExtra    = errors.New("extract Istanbul Extra from block header of the given block number")
   209  	errNoBlockExist            = errors.New("block with the given block number is not existed")
   210  	errNoBlockNumber           = errors.New("block number is not assigned")
   211  )
   212  
   213  // GetCouncil retrieves the list of authorized validators at the specified block.
   214  func (api *APIExtension) GetCouncil(number *rpc.BlockNumber) ([]common.Address, error) {
   215  	header, err := headerByRpcNumber(api.chain, number)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	blockNumber := header.Number.Uint64()
   221  	if blockNumber == 0 {
   222  		// The committee of genesis block can not be calculated because it requires a previous block.
   223  		istanbulExtra, err := types.ExtractIstanbulExtra(header)
   224  		if err != nil {
   225  			return nil, errExtractIstanbulExtra
   226  		}
   227  		return istanbulExtra.Validators, nil
   228  	}
   229  
   230  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
   231  	if err != nil {
   232  		logger.Error("Failed to get snapshot.", "hash", snap.Hash, "err", err)
   233  		return nil, err
   234  	}
   235  
   236  	return append(snap.validators(), snap.demotedValidators()...), nil
   237  }
   238  
   239  func (api *APIExtension) GetCouncilSize(number *rpc.BlockNumber) (int, error) {
   240  	council, err := api.GetCouncil(number)
   241  	if err == nil {
   242  		return len(council), nil
   243  	} else {
   244  		return -1, err
   245  	}
   246  }
   247  
   248  func (api *APIExtension) GetCommittee(number *rpc.BlockNumber) ([]common.Address, error) {
   249  	header, err := headerByRpcNumber(api.chain, number)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	blockNumber := header.Number.Uint64()
   255  	if blockNumber == 0 {
   256  		// The committee of genesis block can not be calculated because it requires a previous block.
   257  		istanbulExtra, err := types.ExtractIstanbulExtra(header)
   258  		if err != nil {
   259  			return nil, errExtractIstanbulExtra
   260  		}
   261  		return istanbulExtra.Validators, nil
   262  	}
   263  
   264  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64()-1, header.ParentHash, nil, false)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	round := header.Round()
   269  	view := &istanbul.View{
   270  		Sequence: new(big.Int).SetUint64(blockNumber),
   271  		Round:    new(big.Int).SetUint64(uint64(round)),
   272  	}
   273  
   274  	// get the proposer of this block.
   275  	proposer, err := ecrecover(header)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	parentHash := header.ParentHash
   281  
   282  	// get the committee list of this block at the view (blockNumber, round)
   283  	committee := snap.ValSet.SubListWithProposer(parentHash, proposer, view)
   284  	addresses := make([]common.Address, len(committee))
   285  	for i, v := range committee {
   286  		addresses[i] = v.Address()
   287  	}
   288  
   289  	return addresses, nil
   290  }
   291  
   292  func (api *APIExtension) GetCommitteeSize(number *rpc.BlockNumber) (int, error) {
   293  	committee, err := api.GetCommittee(number)
   294  	if err == nil {
   295  		return len(committee), nil
   296  	} else {
   297  		return -1, err
   298  	}
   299  }
   300  
   301  func (api *APIExtension) makeRPCBlockOutput(b *types.Block,
   302  	cInfo consensus.ConsensusInfo, transactions types.Transactions, receipts types.Receipts,
   303  ) map[string]interface{} {
   304  	head := b.Header() // copies the header once
   305  	hash := head.Hash()
   306  
   307  	td := big.NewInt(0)
   308  	if bc, ok := api.chain.(*blockchain.BlockChain); ok {
   309  		td = bc.GetTd(hash, b.NumberU64())
   310  	}
   311  	r, err := klaytnApi.RpcOutputBlock(b, td, false, false, api.chain.Config().IsEthTxTypeForkEnabled(b.Header().Number))
   312  	if err != nil {
   313  		logger.Error("failed to RpcOutputBlock", "err", err)
   314  		return nil
   315  	}
   316  
   317  	// make transactions
   318  	numTxs := len(transactions)
   319  	rpcTransactions := make([]map[string]interface{}, numTxs)
   320  	for i, tx := range transactions {
   321  		rpcTransactions[i] = klaytnApi.RpcOutputReceipt(head, tx, hash, head.Number.Uint64(), uint64(i), receipts[i])
   322  	}
   323  
   324  	r["committee"] = cInfo.Committee
   325  	r["proposer"] = cInfo.Proposer
   326  	r["round"] = cInfo.Round
   327  	r["originProposer"] = cInfo.OriginProposer
   328  	r["transactions"] = rpcTransactions
   329  
   330  	return r
   331  }
   332  
   333  // TODO-Klaytn: This API functions should be managed with API functions with namespace "klay"
   334  func (api *APIExtension) GetBlockWithConsensusInfoByNumber(number *rpc.BlockNumber) (map[string]interface{}, error) {
   335  	b, ok := api.chain.(*blockchain.BlockChain)
   336  	if !ok {
   337  		logger.Error("chain is not a type of blockchain.BlockChain", "type", reflect.TypeOf(api.chain))
   338  		return nil, errInternalError
   339  	}
   340  	var block *types.Block
   341  	var blockNumber uint64
   342  
   343  	if number == nil {
   344  		logger.Trace("block number is not assigned")
   345  		return nil, errNoBlockNumber
   346  	}
   347  
   348  	if *number == rpc.PendingBlockNumber {
   349  		logger.Trace("Cannot get consensus information of the PendingBlock.")
   350  		return nil, errPendingNotAllowed
   351  	}
   352  
   353  	if *number == rpc.LatestBlockNumber {
   354  		block = b.CurrentBlock()
   355  		blockNumber = block.NumberU64()
   356  	} else {
   357  		// rpc.EarliestBlockNumber == 0, no need to treat it as a special case.
   358  		blockNumber = uint64(number.Int64())
   359  		block = b.GetBlockByNumber(blockNumber)
   360  	}
   361  
   362  	if block == nil {
   363  		logger.Trace("Finding a block by number failed.", "blockNum", blockNumber)
   364  		return nil, fmt.Errorf("the block does not exist (block number: %d)", blockNumber)
   365  	}
   366  	blockHash := block.Hash()
   367  
   368  	cInfo, err := api.istanbul.GetConsensusInfo(block)
   369  	if err != nil {
   370  		logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err)
   371  		return nil, errInternalError
   372  	}
   373  
   374  	receipts := b.GetBlockReceiptsInCache(blockHash)
   375  	if receipts == nil {
   376  		receipts = b.GetReceiptsByBlockHash(blockHash)
   377  	}
   378  
   379  	return api.makeRPCBlockOutput(block, cInfo, block.Transactions(), receipts), nil
   380  }
   381  
   382  func (api *APIExtension) GetBlockWithConsensusInfoByNumberRange(start *rpc.BlockNumber, end *rpc.BlockNumber) (map[string]interface{}, error) {
   383  	blocks := make(map[string]interface{})
   384  
   385  	if start == nil || end == nil {
   386  		logger.Trace("the range values should not be nil.", "start", start, "end", end)
   387  		return nil, errRangeNil
   388  	}
   389  
   390  	// check error status.
   391  	s := start.Int64()
   392  	e := end.Int64()
   393  	if s < 0 {
   394  		logger.Trace("start should be positive", "start", s)
   395  		return nil, errStartNotPositive
   396  	}
   397  
   398  	eChain := api.chain.CurrentHeader().Number.Int64()
   399  	if e > eChain {
   400  		logger.Trace("end should be smaller than the lastest block number", "end", end, "eChain", eChain)
   401  		return nil, errEndLargetThanLatest
   402  	}
   403  
   404  	if s > e {
   405  		logger.Trace("start should be smaller than end", "start", s, "end", e)
   406  		return nil, errStartLargerThanEnd
   407  	}
   408  
   409  	if (e - s) > 50 {
   410  		logger.Trace("number of requested blocks should be smaller than 50", "start", s, "end", e)
   411  		return nil, errRequestedBlocksTooLarge
   412  	}
   413  
   414  	// gather s~e blocks
   415  	for i := s; i <= e; i++ {
   416  		strIdx := fmt.Sprintf("0x%x", i)
   417  
   418  		blockNum := rpc.BlockNumber(i)
   419  		b, err := api.GetBlockWithConsensusInfoByNumber(&blockNum)
   420  		if err != nil {
   421  			logger.Error("error on GetBlockWithConsensusInfoByNumber", "err", err)
   422  			blocks[strIdx] = nil
   423  		} else {
   424  			blocks[strIdx] = b
   425  		}
   426  	}
   427  
   428  	return blocks, nil
   429  }
   430  
   431  func (api *APIExtension) GetBlockWithConsensusInfoByHash(blockHash common.Hash) (map[string]interface{}, error) {
   432  	b, ok := api.chain.(*blockchain.BlockChain)
   433  	if !ok {
   434  		logger.Error("chain is not a type of blockchain.Blockchain, returning...", "type", reflect.TypeOf(api.chain))
   435  		return nil, errInternalError
   436  	}
   437  
   438  	block := b.GetBlockByHash(blockHash)
   439  	if block == nil {
   440  		logger.Trace("Finding a block failed.", "blockHash", blockHash)
   441  		return nil, fmt.Errorf("the block does not exist (block hash: %s)", blockHash.String())
   442  	}
   443  
   444  	cInfo, err := api.istanbul.GetConsensusInfo(block)
   445  	if err != nil {
   446  		logger.Error("Getting the proposer and validators failed.", "blockHash", blockHash, "err", err)
   447  		return nil, errInternalError
   448  	}
   449  
   450  	receipts := b.GetBlockReceiptsInCache(blockHash)
   451  	if receipts == nil {
   452  		receipts = b.GetReceiptsByBlockHash(blockHash)
   453  	}
   454  
   455  	return api.makeRPCBlockOutput(block, cInfo, block.Transactions(), receipts), nil
   456  }
   457  
   458  func (api *API) GetTimeout() uint64 {
   459  	return istanbul.DefaultConfig.Timeout
   460  }
   461  
   462  // Retrieve the header at requested block number
   463  func headerByRpcNumber(chain consensus.ChainReader, number *rpc.BlockNumber) (*types.Header, error) {
   464  	var header *types.Header
   465  	if number == nil || *number == rpc.LatestBlockNumber {
   466  		header = chain.CurrentHeader()
   467  	} else if *number == rpc.PendingBlockNumber {
   468  		logger.Trace("Cannot get snapshot of the pending block.", "number", number)
   469  		return nil, errPendingNotAllowed
   470  	} else {
   471  		header = chain.GetHeaderByNumber(uint64(number.Int64()))
   472  	}
   473  	// Ensure we have an actually valid block and return its snapshot
   474  	if header == nil {
   475  		return nil, errUnknownBlock
   476  	}
   477  	return header, nil
   478  }