github.com/amazechain/amc@v0.1.3/internal/consensus/apos/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 apos
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"github.com/amazechain/amc/contracts/deposit"
    25  	"github.com/amazechain/amc/modules/rawdb"
    26  	"github.com/amazechain/amc/turbo/rpchelper"
    27  	"github.com/holiman/uint256"
    28  	"github.com/ledgerwatch/erigon-lib/kv"
    29  
    30  	"github.com/amazechain/amc/common/block"
    31  	"github.com/amazechain/amc/common/hexutil"
    32  	"github.com/amazechain/amc/common/types"
    33  	"github.com/amazechain/amc/internal/avm/common"
    34  	mvm_types "github.com/amazechain/amc/internal/avm/types"
    35  	"github.com/amazechain/amc/internal/consensus"
    36  	"github.com/amazechain/amc/modules/rpc/jsonrpc"
    37  )
    38  
    39  const maxSearchBlock = 1000
    40  
    41  type MinedBlock struct {
    42  	BlockNumber *uint256.Int `json:"blockNumber"`
    43  	Timestamp   uint64       `json:"timestamp"`
    44  	Reward      *uint256.Int `json:"reward"`
    45  }
    46  type MinedBlockResponse struct {
    47  	MinedBlocks        []MinedBlock `json:"minedBlocks"`
    48  	CurrentBlockNumber *uint256.Int `json:"currentBlockNumber"`
    49  }
    50  
    51  type VerifiedBlockResponse struct {
    52  	MinedBlocks []MinedBlock `json:"minedBlocks"`
    53  	Total       *uint256.Int `json:"totalBlocks"`
    54  }
    55  
    56  // API is a user facing jsonrpc API to allow controlling the signer and voting
    57  // mechanisms of the proof-of-authority scheme.
    58  type API struct {
    59  	chain consensus.ChainReader
    60  	apos  *APos
    61  }
    62  
    63  // GetSnapshot retrieves the state snapshot at a given block.
    64  func (api *API) GetSnapshot(number *jsonrpc.BlockNumber) (*Snapshot, error) {
    65  	// Retrieve the requested block number (or current if none requested)
    66  	var header block.IHeader
    67  	if number == nil || *number == jsonrpc.LatestBlockNumber {
    68  		header = api.chain.CurrentBlock().Header()
    69  	} else {
    70  		header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
    71  	}
    72  	// Ensure we have an actually valid block and return its snapshot
    73  	if header == nil {
    74  		return nil, errUnknownBlock
    75  	}
    76  	return api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    77  }
    78  
    79  // GetSnapshotAtHash retrieves the state snapshot at a given block.
    80  func (api *API) GetSnapshotAtHash(hash types.Hash) (*Snapshot, error) {
    81  	header, _ := api.chain.GetHeaderByHash(hash)
    82  	if header == nil {
    83  		return nil, errUnknownBlock
    84  	}
    85  	return api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
    86  }
    87  
    88  // GetSigners retrieves the list of authorized signers at the specified block.
    89  func (api *API) GetSigners(number *jsonrpc.BlockNumber) ([]common.Address, error) {
    90  	// Retrieve the requested block number (or current if none requested)
    91  	var header block.IHeader
    92  	if number == nil || *number == jsonrpc.LatestBlockNumber {
    93  		header = api.chain.CurrentBlock().Header()
    94  	} else {
    95  		header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
    96  	}
    97  	// Ensure we have an actually valid block and return the signers from its snapshot
    98  	if header == nil {
    99  		return nil, errUnknownBlock
   100  	}
   101  	snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	signers := snap.signers()
   107  	ethSigners := make([]common.Address, len(signers))
   108  	for i, signer := range signers {
   109  		ethSigners[i] = *mvm_types.FromAmcAddress(&signer)
   110  	}
   111  	return ethSigners, nil
   112  }
   113  
   114  // GetSignersAtHash retrieves the list of authorized signers at the specified block.
   115  func (api *API) GetSignersAtHash(hash types.Hash) ([]common.Address, error) {
   116  	header, _ := api.chain.GetHeaderByHash(hash)
   117  	if header == nil {
   118  		return nil, errUnknownBlock
   119  	}
   120  	snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	signers := snap.signers()
   125  	ethSigners := make([]common.Address, len(signers))
   126  	for i, signer := range signers {
   127  		ethSigners[i] = *mvm_types.FromAmcAddress(&signer)
   128  	}
   129  	return ethSigners, nil
   130  }
   131  
   132  // Proposals returns the current proposals the node tries to uphold and vote on.
   133  func (api *API) Proposals() map[common.Address]bool {
   134  	api.apos.lock.RLock()
   135  	defer api.apos.lock.RUnlock()
   136  
   137  	proposals := make(map[common.Address]bool)
   138  	for address, auth := range api.apos.proposals {
   139  		proposals[*mvm_types.FromAmcAddress(&address)] = auth
   140  	}
   141  	return proposals
   142  }
   143  
   144  // Propose injects a new authorization proposal that the signer will attempt to
   145  // push through.
   146  func (api *API) Propose(address common.Address, auth bool) {
   147  	api.apos.lock.Lock()
   148  	defer api.apos.lock.Unlock()
   149  
   150  	api.apos.proposals[*mvm_types.ToAmcAddress(&address)] = auth
   151  }
   152  
   153  // Discard drops a currently running proposal, stopping the signer from casting
   154  // further votes (either for or against).
   155  func (api *API) Discard(address common.Address) {
   156  	api.apos.lock.Lock()
   157  	defer api.apos.lock.Unlock()
   158  
   159  	delete(api.apos.proposals, *mvm_types.ToAmcAddress(&address))
   160  }
   161  
   162  type status struct {
   163  	InturnPercent float64               `json:"inturnPercent"`
   164  	SigningStatus map[types.Address]int `json:"sealerActivity"`
   165  	NumBlocks     uint64                `json:"numBlocks"`
   166  }
   167  
   168  // Status returns the status of the last N blocks,
   169  // - the number of active signers,
   170  // - the number of signers,
   171  // - the percentage of in-turn blocks
   172  func (api *API) Status() (*status, error) {
   173  	var (
   174  		numBlocks = uint64(64)
   175  		header    = api.chain.CurrentBlock().Header()
   176  		diff      = uint64(0)
   177  		optimals  = 0
   178  	)
   179  	snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	var (
   184  		signers = snap.signers()
   185  		end     = header.Number64().Uint64()
   186  		start   = end - numBlocks
   187  	)
   188  	if numBlocks > end {
   189  		start = 1
   190  		numBlocks = end - start
   191  	}
   192  	signStatus := make(map[types.Address]int)
   193  	for _, s := range signers {
   194  		signStatus[s] = 0
   195  	}
   196  	for n := start; n < end; n++ {
   197  		h := api.chain.GetHeaderByNumber(uint256.NewInt(n))
   198  		block := api.chain.GetBlock(h.Hash(), n)
   199  		if h == nil {
   200  			return nil, fmt.Errorf("missing block %d", n)
   201  		}
   202  		if block.Difficulty().Cmp(diffInTurn) == 0 {
   203  			optimals++
   204  		}
   205  		diff += block.Difficulty().Uint64()
   206  		sealer, err := api.apos.Author(h)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  		signStatus[sealer]++
   211  	}
   212  	return &status{
   213  		InturnPercent: float64(100*optimals) / float64(numBlocks),
   214  		SigningStatus: signStatus,
   215  		NumBlocks:     numBlocks,
   216  	}, nil
   217  }
   218  
   219  type blockNumberOrHashOrRLP struct {
   220  	*jsonrpc.BlockNumberOrHash
   221  	RLP hexutil.Bytes `json:"rlp,omitempty"`
   222  }
   223  
   224  func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
   225  	bnOrHash := new(jsonrpc.BlockNumberOrHash)
   226  	// Try to unmarshal bNrOrHash
   227  	if err := bnOrHash.UnmarshalJSON(data); err == nil {
   228  		sb.BlockNumberOrHash = bnOrHash
   229  		return nil
   230  	}
   231  	// Try to unmarshal RLP
   232  	var input string
   233  	if err := json.Unmarshal(data, &input); err != nil {
   234  		return err
   235  	}
   236  	blob, err := hexutil.Decode(input)
   237  	if err != nil {
   238  		return err
   239  	}
   240  	sb.RLP = blob
   241  	return nil
   242  }
   243  
   244  // GetSigner returns the signer for a specific Apos block.
   245  // Can be called with either a blocknumber, blockhash or an rlp encoded blob.
   246  // The RLP encoded blob can either be a block or a header.
   247  func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (types.Address, error) {
   248  	if len(rlpOrBlockNr.RLP) == 0 {
   249  		blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
   250  		var header block.IHeader
   251  		if blockNrOrHash == nil {
   252  			header = api.chain.CurrentBlock().Header()
   253  		} else if hash, ok := blockNrOrHash.Hash(); ok {
   254  			header, _ = api.chain.GetHeaderByHash(hash)
   255  		} else if number, ok := blockNrOrHash.Number(); ok {
   256  			header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64())))
   257  		}
   258  		if header == nil {
   259  			return types.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String())
   260  		}
   261  		return api.apos.Author(header)
   262  	}
   263  
   264  	return types.Address{}, fmt.Errorf("do not support rlp")
   265  }
   266  
   267  // GetRewards
   268  func (api *API) GetRewards(address common.Address, from jsonrpc.BlockNumberOrHash, to jsonrpc.BlockNumberOrHash) (resp *RewardResponse, err error) {
   269  
   270  	var (
   271  		resolvedFromBlock *uint256.Int
   272  		resolvedToBlock   *uint256.Int
   273  	)
   274  
   275  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   276  		resolvedFromBlock, _, err = rpchelper.GetCanonicalBlockNumber(from, tx)
   277  		resolvedToBlock, _, err = rpchelper.GetCanonicalBlockNumber(to, tx)
   278  		return nil
   279  	})
   280  
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	rewardService := newReward(api.apos.chainConfig)
   286  	resp, err = rewardService.GetRewards(*mvm_types.ToAmcAddress(&address), resolvedFromBlock, resolvedToBlock, api.chain.GetBlockByNumber)
   287  
   288  	return resp, err
   289  }
   290  
   291  // GetRewards
   292  func (api *API) GetDepositInfo(address common.Address) (*deposit.Info, error) {
   293  
   294  	addr := *mvm_types.ToAmcAddress(&address)
   295  
   296  	info := new(deposit.Info)
   297  	var err error
   298  
   299  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   300  		info = deposit.GetDepositInfo(tx, addr)
   301  		return nil
   302  	})
   303  
   304  	return info, err
   305  }
   306  
   307  // GetRewards todo:needs check
   308  func (api *API) GetBlockRewards(blockNr jsonrpc.BlockNumberOrHash) (resp []*block.Reward, err error) {
   309  	var (
   310  		resolvedBlockNr *uint256.Int
   311  		hash            types.Hash
   312  	)
   313  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   314  		resolvedBlockNr, hash, err = rpchelper.GetCanonicalBlockNumber(blockNr, tx)
   315  		if err != nil {
   316  			return err
   317  		}
   318  		//header := rawdb.ReadHeader(tx, hash, resolvedBlockNr.Uint64())
   319  		body := rawdb.ReadBlock(tx, hash, resolvedBlockNr.Uint64())
   320  		if body == nil {
   321  			err = errors.New("cannot find block body")
   322  			return err
   323  		}
   324  		resp = body.Body().Reward()
   325  		return nil
   326  	})
   327  	return resp, err
   328  }
   329  
   330  // getHeader search header by BlockNumberOrHash
   331  func (api *API) getHeader(from jsonrpc.BlockNumberOrHash) (currentHeader block.IHeader) {
   332  	//
   333  	if blockNr, ok := from.Number(); ok {
   334  		if blockNr == jsonrpc.LatestBlockNumber || blockNr == jsonrpc.PendingBlockNumber {
   335  			currentHeader = api.chain.CurrentBlock().Header()
   336  		} else if blockNr < jsonrpc.EarliestBlockNumber {
   337  			currentHeader = api.chain.GetHeaderByNumber(uint256.NewInt(0))
   338  		} else {
   339  			currentHeader = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(blockNr.Int64())))
   340  		}
   341  	} else if hash, ok := from.Hash(); ok {
   342  		currentHeader, _ = api.chain.GetHeaderByHash(hash)
   343  	} else {
   344  		currentHeader = api.chain.CurrentBlock().Header()
   345  	}
   346  	return
   347  }
   348  
   349  // GetTasks
   350  func (api *API) GetMinedBlock(address common.Address, from jsonrpc.BlockNumberOrHash, wantCount uint64) (*MinedBlockResponse, error) {
   351  
   352  	addr := *mvm_types.ToAmcAddress(&address)
   353  	var (
   354  		err           error
   355  		searchCount   int
   356  		findCount     uint64
   357  		currentHeader block.IHeader
   358  		currentBlock  block.IBlock
   359  		depositInfo   *deposit.Info
   360  	)
   361  
   362  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   363  		depositInfo = deposit.GetDepositInfo(tx, addr)
   364  		return nil
   365  	})
   366  	if depositInfo == nil {
   367  		return nil, fmt.Errorf("address do not have depositInfo")
   368  	}
   369  	//
   370  	currentHeader = api.getHeader(from)
   371  	if currentHeader == nil {
   372  		return nil, err
   373  	}
   374  
   375  	//
   376  	currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64())
   377  	searchCount = 0
   378  	findCount = 0
   379  	minedBlocks := make([]MinedBlock, 0)
   380  
   381  	for i := currentHeader.Number64().Uint64(); i >= 0; i-- {
   382  		verifier := currentBlock.Body().Verifier()
   383  		for _, verify := range verifier {
   384  			if addr == verify.Address {
   385  				minedBlocks = append(minedBlocks, MinedBlock{
   386  					BlockNumber: currentBlock.Number64(),
   387  					Timestamp:   currentBlock.Time(),
   388  					Reward:      depositInfo.RewardPerBlock,
   389  				})
   390  				findCount++
   391  				if findCount >= wantCount {
   392  					goto Finish
   393  				}
   394  			}
   395  		}
   396  		searchCount++
   397  		if searchCount >= maxSearchBlock {
   398  			break
   399  		}
   400  		currentHeader = api.chain.GetHeaderByNumber(new(uint256.Int).SubUint64(currentBlock.Number64(), 1))
   401  		if currentHeader == nil {
   402  			break
   403  		}
   404  		currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64())
   405  	}
   406  
   407  Finish:
   408  	return &MinedBlockResponse{
   409  		MinedBlocks:        minedBlocks,
   410  		CurrentBlockNumber: currentBlock.Number64(),
   411  	}, nil
   412  }
   413  
   414  // VerifiedBlock
   415  func (api *API) VerifiedBlock(address common.Address, from jsonrpc.BlockNumberOrHash, wantCount uint64, to *jsonrpc.BlockNumber) (*VerifiedBlockResponse, error) {
   416  
   417  	addr := *mvm_types.ToAmcAddress(&address)
   418  	var (
   419  		err           error
   420  		searchCount   int
   421  		findCount     uint64
   422  		currentHeader block.IHeader
   423  		currentBlock  block.IBlock
   424  		depositInfo   *deposit.Info
   425  	)
   426  
   427  	if to.Int64() <= 0 {
   428  		return nil, fmt.Errorf("'To' block number must be greater than 0")
   429  	}
   430  
   431  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   432  		depositInfo = deposit.GetDepositInfo(tx, addr)
   433  		return nil
   434  	})
   435  	if depositInfo == nil {
   436  		return nil, fmt.Errorf("address do not have depositInfo")
   437  	}
   438  	//
   439  	currentHeader = api.getHeader(from)
   440  	if currentHeader == nil {
   441  		return nil, err
   442  	}
   443  
   444  	//
   445  	currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64())
   446  	searchCount = 0
   447  	findCount = 0
   448  	minedBlocks := make([]MinedBlock, 0)
   449  
   450  	for i := currentHeader.Number64().Uint64(); i >= 0; i-- {
   451  		verifier := currentBlock.Body().Verifier()
   452  		for _, verify := range verifier {
   453  			if addr == verify.Address {
   454  				minedBlocks = append(minedBlocks, MinedBlock{
   455  					BlockNumber: currentBlock.Number64(),
   456  					Timestamp:   currentBlock.Time(),
   457  					Reward:      depositInfo.RewardPerBlock,
   458  				})
   459  				findCount++
   460  			}
   461  		}
   462  		if findCount >= wantCount {
   463  			break
   464  		}
   465  		if uint64(*to) == currentBlock.Number64().Uint64() {
   466  			break
   467  		}
   468  		searchCount++
   469  		if searchCount >= int(api.apos.config.RewardEpoch) {
   470  			break
   471  		}
   472  		currentHeader = api.chain.GetHeaderByNumber(new(uint256.Int).SubUint64(currentBlock.Number64(), 1))
   473  		if currentHeader == nil {
   474  			break
   475  		}
   476  		currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64())
   477  	}
   478  
   479  	return &VerifiedBlockResponse{
   480  		MinedBlocks: minedBlocks,
   481  		Total:       uint256.NewInt(findCount),
   482  	}, nil
   483  }
   484  
   485  func (api *API) GetAccountRewardUnpaid(address types.Address) (val *uint256.Int, err error) {
   486  	rewardService := newReward(api.apos.chainConfig)
   487  
   488  	api.apos.db.View(context.Background(), func(tx kv.Tx) error {
   489  		val, err = rewardService.getAccountRewardUnpaid(tx, address)
   490  		return err
   491  	})
   492  	return
   493  }