github.com/amazechain/amc@v0.1.3/internal/consensus/apoa/api.go (about)

     1  // Copyright 2022 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package apoa
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"github.com/amazechain/amc/common/block"
    23  	"github.com/amazechain/amc/common/hexutil"
    24  	"github.com/amazechain/amc/common/types"
    25  	"github.com/amazechain/amc/internal/avm/common"
    26  	mvm_types "github.com/amazechain/amc/internal/avm/types"
    27  	"github.com/amazechain/amc/internal/consensus"
    28  	"github.com/amazechain/amc/modules/rpc/jsonrpc"
    29  	"github.com/holiman/uint256"
    30  )
    31  
    32  // API is a user facing jsonrpc API to allow controlling the signer and voting
    33  // mechanisms of the proof-of-authority scheme.
    34  type API struct {
    35  	chain consensus.ChainReader
    36  	apoa  *Apoa
    37  }
    38  
    39  // GetSnapshot retrieves the state snapshot at a given block.
    40  func (api *API) GetSnapshot(number *jsonrpc.BlockNumber) (*Snapshot, error) {
    41  	// Retrieve the requested block number (or current if none requested)
    42  	var header block.IHeader
    43  	if number == nil || *number == jsonrpc.LatestBlockNumber {
    44  		header = api.chain.CurrentBlock().Header()
    45  	} else {
    46  		header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
    47  	}
    48  	// Ensure we have an actually valid block and return its snapshot
    49  	if header == nil {
    50  		return nil, errUnknownBlock
    51  	}
    52  	return api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    53  }
    54  
    55  // GetSnapshotAtHash retrieves the state snapshot at a given block.
    56  func (api *API) GetSnapshotAtHash(hash types.Hash) (*Snapshot, error) {
    57  	header, _ := api.chain.GetHeaderByHash(hash)
    58  	if header == nil {
    59  		return nil, errUnknownBlock
    60  	}
    61  	return api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    62  }
    63  
    64  // GetSigners retrieves the list of authorized signers at the specified block.
    65  func (api *API) GetSigners(number *jsonrpc.BlockNumber) ([]common.Address, error) {
    66  	// Retrieve the requested block number (or current if none requested)
    67  	var header block.IHeader
    68  	if number == nil || *number == jsonrpc.LatestBlockNumber {
    69  		header = api.chain.CurrentBlock().Header()
    70  	} else {
    71  		header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
    72  	}
    73  	// Ensure we have an actually valid block and return the signers from its snapshot
    74  	if header == nil {
    75  		return nil, errUnknownBlock
    76  	}
    77  	snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	signers := snap.signers()
    83  	ethSigners := make([]common.Address, len(signers))
    84  	for i, signer := range signers {
    85  		ethSigners[i] = *mvm_types.FromAmcAddress(&signer)
    86  	}
    87  	return ethSigners, nil
    88  }
    89  
    90  // GetSignersAtHash retrieves the list of authorized signers at the specified block.
    91  func (api *API) GetSignersAtHash(hash types.Hash) ([]common.Address, error) {
    92  	header, _ := api.chain.GetHeaderByHash(hash)
    93  	if header == nil {
    94  		return nil, errUnknownBlock
    95  	}
    96  	snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	signers := snap.signers()
   101  	ethSigners := make([]common.Address, len(signers))
   102  	for i, signer := range signers {
   103  		ethSigners[i] = *mvm_types.FromAmcAddress(&signer)
   104  	}
   105  	return ethSigners, nil
   106  }
   107  
   108  // Proposals returns the current proposals the node tries to uphold and vote on.
   109  func (api *API) Proposals() map[common.Address]bool {
   110  	api.apoa.lock.RLock()
   111  	defer api.apoa.lock.RUnlock()
   112  
   113  	proposals := make(map[common.Address]bool)
   114  	for address, auth := range api.apoa.proposals {
   115  		proposals[*mvm_types.FromAmcAddress(&address)] = auth
   116  	}
   117  	return proposals
   118  }
   119  
   120  // Propose injects a new authorization proposal that the signer will attempt to
   121  // push through.
   122  func (api *API) Propose(address common.Address, auth bool) {
   123  	api.apoa.lock.Lock()
   124  	defer api.apoa.lock.Unlock()
   125  
   126  	api.apoa.proposals[*mvm_types.ToAmcAddress(&address)] = auth
   127  }
   128  
   129  // Discard drops a currently running proposal, stopping the signer from casting
   130  // further votes (either for or against).
   131  func (api *API) Discard(address common.Address) {
   132  	api.apoa.lock.Lock()
   133  	defer api.apoa.lock.Unlock()
   134  
   135  	delete(api.apoa.proposals, *mvm_types.ToAmcAddress(&address))
   136  }
   137  
   138  type status struct {
   139  	InturnPercent float64               `json:"inturnPercent"`
   140  	SigningStatus map[types.Address]int `json:"sealerActivity"`
   141  	NumBlocks     uint64                `json:"numBlocks"`
   142  }
   143  
   144  // Status returns the status of the last N blocks,
   145  // - the number of active signers,
   146  // - the number of signers,
   147  // - the percentage of in-turn blocks
   148  func (api *API) Status() (*status, error) {
   149  	var (
   150  		numBlocks = uint64(64)
   151  		header    = api.chain.CurrentBlock().Header()
   152  		diff      = uint64(0)
   153  		optimals  = 0
   154  	)
   155  	snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	var (
   160  		signers = snap.signers()
   161  		end     = header.Number64().Uint64()
   162  		start   = end - numBlocks
   163  	)
   164  	if numBlocks > end {
   165  		start = 1
   166  		numBlocks = end - start
   167  	}
   168  	signStatus := make(map[types.Address]int)
   169  	for _, s := range signers {
   170  		signStatus[s] = 0
   171  	}
   172  	for n := start; n < end; n++ {
   173  		h := api.chain.GetHeaderByNumber(uint256.NewInt(n))
   174  		block := api.chain.GetBlock(h.Hash(), n)
   175  		if h == nil {
   176  			return nil, fmt.Errorf("missing block %d", n)
   177  		}
   178  		if block.Difficulty().Cmp(diffInTurn) == 0 {
   179  			optimals++
   180  		}
   181  		diff += block.Difficulty().Uint64()
   182  		sealer, err := api.apoa.Author(h)
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  		signStatus[sealer]++
   187  	}
   188  	return &status{
   189  		InturnPercent: float64(100*optimals) / float64(numBlocks),
   190  		SigningStatus: signStatus,
   191  		NumBlocks:     numBlocks,
   192  	}, nil
   193  }
   194  
   195  type blockNumberOrHashOrRLP struct {
   196  	*jsonrpc.BlockNumberOrHash
   197  	RLP hexutil.Bytes `json:"rlp,omitempty"`
   198  }
   199  
   200  func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
   201  	bnOrHash := new(jsonrpc.BlockNumberOrHash)
   202  	// Try to unmarshal bNrOrHash
   203  	if err := bnOrHash.UnmarshalJSON(data); err == nil {
   204  		sb.BlockNumberOrHash = bnOrHash
   205  		return nil
   206  	}
   207  	// Try to unmarshal RLP
   208  	var input string
   209  	if err := json.Unmarshal(data, &input); err != nil {
   210  		return err
   211  	}
   212  	blob, err := hexutil.Decode(input)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	sb.RLP = blob
   217  	return nil
   218  }
   219  
   220  // GetSigner returns the signer for a specific apoa block.
   221  // Can be called with either a blocknumber, blockhash or an rlp encoded blob.
   222  // The RLP encoded blob can either be a block or a header.
   223  func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (types.Address, error) {
   224  	if len(rlpOrBlockNr.RLP) == 0 {
   225  		blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
   226  		var header block.IHeader
   227  		if blockNrOrHash == nil {
   228  			header = api.chain.CurrentBlock().Header()
   229  		} else if hash, ok := blockNrOrHash.Hash(); ok {
   230  			header, _ = api.chain.GetHeaderByHash(hash)
   231  		} else if number, ok := blockNrOrHash.Number(); ok {
   232  			header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
   233  		}
   234  		if header == nil {
   235  			return types.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String())
   236  		}
   237  		return api.apoa.Author(header)
   238  	}
   239  
   240  	return types.Address{}, fmt.Errorf("do not support rlp")
   241  }