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