github.com/xfers/quorum@v21.1.0+incompatible/consensus/istanbul/backend/api.go (about)

     1  // Copyright 2017 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 backend
    18  
    19  import (
    20  	"errors"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus"
    24  	"github.com/ethereum/go-ethereum/core/types"
    25  	"github.com/ethereum/go-ethereum/rpc"
    26  )
    27  
    28  // API is a user facing RPC API to dump Istanbul state
    29  type API struct {
    30  	chain    consensus.ChainReader
    31  	istanbul *backend
    32  }
    33  
    34  // BlockSigners is contains who created and who signed a particular block, denoted by its number and hash
    35  type BlockSigners struct {
    36  	Number     uint64
    37  	Hash       common.Hash
    38  	Author     common.Address
    39  	Committers []common.Address
    40  }
    41  
    42  type Status struct {
    43  	SigningStatus map[common.Address]int `json:"sealerActivity"`
    44  	NumBlocks     uint64                 `json:"numBlocks"`
    45  }
    46  
    47  // NodeAddress returns the public address that is used to sign block headers in IBFT
    48  func (api *API) NodeAddress() common.Address {
    49  	return api.istanbul.Address()
    50  }
    51  
    52  // GetSignersFromBlock returns the signers and minter for a given block number, or the
    53  // latest block available if none is specified
    54  func (api *API) GetSignersFromBlock(number *rpc.BlockNumber) (*BlockSigners, error) {
    55  	// Retrieve the requested block number (or current if none requested)
    56  	var header *types.Header
    57  	if number == nil || *number == rpc.LatestBlockNumber {
    58  		header = api.chain.CurrentHeader()
    59  	} else {
    60  		header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
    61  	}
    62  
    63  	if header == nil {
    64  		return nil, errUnknownBlock
    65  	}
    66  
    67  	return api.signers(header)
    68  }
    69  
    70  // GetSignersFromBlockByHash returns the signers and minter for a given block hash
    71  func (api *API) GetSignersFromBlockByHash(hash common.Hash) (*BlockSigners, error) {
    72  	header := api.chain.GetHeaderByHash(hash)
    73  	if header == nil {
    74  		return nil, errUnknownBlock
    75  	}
    76  
    77  	return api.signers(header)
    78  }
    79  
    80  func (api *API) signers(header *types.Header) (*BlockSigners, error) {
    81  	author, err := api.istanbul.Author(header)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	committers, err := api.istanbul.Signers(header)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	return &BlockSigners{
    92  		Number:     header.Number.Uint64(),
    93  		Hash:       header.Hash(),
    94  		Author:     author,
    95  		Committers: committers,
    96  	}, nil
    97  }
    98  
    99  // GetSnapshot retrieves the state snapshot at a given block.
   100  func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
   101  	// Retrieve the requested block number (or current if none requested)
   102  	var header *types.Header
   103  	if number == nil || *number == rpc.LatestBlockNumber {
   104  		header = api.chain.CurrentHeader()
   105  	} else {
   106  		header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
   107  	}
   108  	// Ensure we have an actually valid block and return its snapshot
   109  	if header == nil {
   110  		return nil, errUnknownBlock
   111  	}
   112  	return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
   113  }
   114  
   115  // GetSnapshotAtHash retrieves the state snapshot at a given block.
   116  func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
   117  	header := api.chain.GetHeaderByHash(hash)
   118  	if header == nil {
   119  		return nil, errUnknownBlock
   120  	}
   121  	return api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
   122  }
   123  
   124  // GetValidators retrieves the list of authorized validators at the specified block.
   125  func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) {
   126  	// Retrieve the requested block number (or current if none requested)
   127  	var header *types.Header
   128  	if number == nil || *number == rpc.LatestBlockNumber {
   129  		header = api.chain.CurrentHeader()
   130  	} else {
   131  		header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
   132  	}
   133  	// Ensure we have an actually valid block and return the validators from its snapshot
   134  	if header == nil {
   135  		return nil, errUnknownBlock
   136  	}
   137  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return snap.validators(), nil
   142  }
   143  
   144  // GetValidatorsAtHash retrieves the state snapshot at a given block.
   145  func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error) {
   146  	header := api.chain.GetHeaderByHash(hash)
   147  	if header == nil {
   148  		return nil, errUnknownBlock
   149  	}
   150  	snap, err := api.istanbul.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	return snap.validators(), nil
   155  }
   156  
   157  // Candidates returns the current candidates the node tries to uphold and vote on.
   158  func (api *API) Candidates() map[common.Address]bool {
   159  	api.istanbul.candidatesLock.RLock()
   160  	defer api.istanbul.candidatesLock.RUnlock()
   161  
   162  	proposals := make(map[common.Address]bool)
   163  	for address, auth := range api.istanbul.candidates {
   164  		proposals[address] = auth
   165  	}
   166  	return proposals
   167  }
   168  
   169  // Propose injects a new authorization candidate that the validator will attempt to
   170  // push through.
   171  func (api *API) Propose(address common.Address, auth bool) {
   172  	api.istanbul.candidatesLock.Lock()
   173  	defer api.istanbul.candidatesLock.Unlock()
   174  
   175  	api.istanbul.candidates[address] = auth
   176  }
   177  
   178  // Discard drops a currently running candidate, stopping the validator from casting
   179  // further votes (either for or against).
   180  func (api *API) Discard(address common.Address) {
   181  	api.istanbul.candidatesLock.Lock()
   182  	defer api.istanbul.candidatesLock.Unlock()
   183  
   184  	delete(api.istanbul.candidates, address)
   185  }
   186  
   187  func (api *API) Status(startBlockNum *rpc.BlockNumber, endBlockNum *rpc.BlockNumber) (*Status, error) {
   188  	var (
   189  		numBlocks   uint64
   190  		header      = api.chain.CurrentHeader()
   191  		start       uint64
   192  		end         uint64
   193  		blockNumber rpc.BlockNumber
   194  	)
   195  	if startBlockNum != nil && endBlockNum == nil {
   196  		return nil, errors.New("pass the end block number")
   197  	}
   198  
   199  	if startBlockNum == nil && endBlockNum != nil {
   200  		return nil, errors.New("pass the start block number")
   201  	}
   202  
   203  	if startBlockNum == nil && endBlockNum == nil {
   204  		numBlocks = uint64(64)
   205  		header = api.chain.CurrentHeader()
   206  		end = header.Number.Uint64()
   207  		start = end - numBlocks
   208  		blockNumber = rpc.BlockNumber(header.Number.Int64())
   209  	} else {
   210  		end = uint64(*endBlockNum)
   211  		start = uint64(*startBlockNum)
   212  		if start > end {
   213  			return nil, errors.New("start block number should be less than end block number")
   214  		}
   215  
   216  		if end > api.chain.CurrentHeader().Number.Uint64() {
   217  			return nil, errors.New("end block number should be less than or equal to current block height")
   218  		}
   219  
   220  		numBlocks = end - start
   221  		header = api.chain.GetHeaderByNumber(end)
   222  		blockNumber = rpc.BlockNumber(end)
   223  	}
   224  
   225  	signers, err := api.GetValidators(&blockNumber)
   226  
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	if numBlocks >= end {
   232  		start = 1
   233  		if end > start {
   234  			numBlocks = end - start
   235  		} else {
   236  			numBlocks = 0
   237  		}
   238  	}
   239  	signStatus := make(map[common.Address]int)
   240  	for _, s := range signers {
   241  		signStatus[s] = 0
   242  	}
   243  
   244  	for n := start; n < end; n++ {
   245  		blockNum := rpc.BlockNumber(int64(n))
   246  		s, _ := api.GetSignersFromBlock(&blockNum)
   247  		signStatus[s.Author]++
   248  
   249  	}
   250  	return &Status{
   251  		SigningStatus: signStatus,
   252  		NumBlocks:     numBlocks,
   253  	}, nil
   254  }
   255  
   256  func (api *API) IsValidator(blockNum *rpc.BlockNumber) (bool, error) {
   257  	var blockNumber rpc.BlockNumber
   258  	if blockNum != nil {
   259  		blockNumber = *blockNum
   260  	} else {
   261  		header := api.chain.CurrentHeader()
   262  		blockNumber = rpc.BlockNumber(header.Number.Int64())
   263  	}
   264  	s, _ := api.GetValidators(&blockNumber)
   265  
   266  	for _, v := range s {
   267  		if v == api.istanbul.address {
   268  			return true, nil
   269  		}
   270  	}
   271  	return false, nil
   272  }