mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Implement api eth.getCandidateStatus
This commit is contained in:
parent
4141ead94e
commit
c8950a1281
4 changed files with 151 additions and 13 deletions
|
|
@ -44,10 +44,17 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultGasPrice = 50 * params.Shannon
|
||||
// statuses of candidates
|
||||
statusMasternode = "MASTERNODE"
|
||||
statusSlashed = "SLASHED"
|
||||
statusProposed = "PROPOSED"
|
||||
|
||||
)
|
||||
|
||||
// PublicEthereumAPI provides an API to access Ethereum related information.
|
||||
|
|
@ -613,7 +620,7 @@ func (s *PublicBlockChainAPI) GetBlockSignersByHash(ctx context.Context, blockHa
|
|||
if err != nil || block == nil {
|
||||
return []common.Address{}, err
|
||||
}
|
||||
masternodes, err := s.GetMasternodes(block, ctx)
|
||||
masternodes, err := s.GetMasternodes(ctx, block)
|
||||
if err != nil || len(masternodes) == 0 {
|
||||
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
|
||||
return []common.Address{}, err
|
||||
|
|
@ -626,7 +633,7 @@ func (s *PublicBlockChainAPI) GetBlockSignersByNumber(ctx context.Context, block
|
|||
if err != nil || block == nil {
|
||||
return []common.Address{}, err
|
||||
}
|
||||
masternodes, err := s.GetMasternodes(block, ctx)
|
||||
masternodes, err := s.GetMasternodes(ctx, block)
|
||||
if err != nil || len(masternodes) == 0 {
|
||||
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
|
||||
return []common.Address{}, err
|
||||
|
|
@ -639,7 +646,7 @@ func (s *PublicBlockChainAPI) GetBlockFinalityByHash(ctx context.Context, blockH
|
|||
if err != nil || block == nil {
|
||||
return int32(0), err
|
||||
}
|
||||
masternodes, err := s.GetMasternodes(block, ctx)
|
||||
masternodes, err := s.GetMasternodes(ctx, block)
|
||||
if err != nil || len(masternodes) == 0 {
|
||||
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
|
||||
return int32(0), err
|
||||
|
|
@ -656,7 +663,7 @@ func (s *PublicBlockChainAPI) GetBlockFinalityByNumber(ctx context.Context, bloc
|
|||
if err != nil || block == nil {
|
||||
return int32(0), err
|
||||
}
|
||||
masternodes, err := s.GetMasternodes(block, ctx)
|
||||
masternodes, err := s.GetMasternodes(ctx, block)
|
||||
if err != nil || len(masternodes) == 0 {
|
||||
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
|
||||
return int32(0), err
|
||||
|
|
@ -667,9 +674,11 @@ func (s *PublicBlockChainAPI) GetBlockFinalityByNumber(ctx context.Context, bloc
|
|||
}
|
||||
return int32(100 * len(blockSigners) / len(masternodes)), err
|
||||
}
|
||||
func (s *PublicBlockChainAPI) GetMasternodes(b *types.Block, ctx context.Context) ([]common.Address, error) {
|
||||
|
||||
// GetMasternodes returns masternodes set at the starting block of epoch of the given block
|
||||
func (s *PublicBlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block) ([]common.Address, error) {
|
||||
var masternodes []common.Address
|
||||
if b.Number().Int64() > 0 {
|
||||
if b.Number().Int64() >= 0 {
|
||||
curBlockNumber := b.Number().Uint64()
|
||||
prevBlockNumber := curBlockNumber + (common.MergeSignRange - (curBlockNumber % common.MergeSignRange))
|
||||
latestBlockNumber := s.b.CurrentBlock().Number().Uint64()
|
||||
|
|
@ -690,6 +699,91 @@ func (s *PublicBlockChainAPI) GetMasternodes(b *types.Block, ctx context.Context
|
|||
return masternodes, nil
|
||||
}
|
||||
|
||||
|
||||
// GetCandidateStatus returns status of the given candidate at a specified epochNumber
|
||||
func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAddress common.Address, epochNumber rpc.EpochNumber) (string, error) {
|
||||
var (
|
||||
block *types.Block
|
||||
masternodes, penaltyList, candidates, proposedList []common.Address
|
||||
penalties []byte
|
||||
err error
|
||||
)
|
||||
block = s.b.CurrentBlock()
|
||||
epoch := s.b.ChainConfig().Posv.Epoch
|
||||
// TODO: we currently support the latest epoch only
|
||||
//if epochNumber == rpc.LatestEpochNumber {
|
||||
// block = s.b.CurrentBlock()
|
||||
//} else {
|
||||
// checkpointNumber := rpc.BlockNumber((uint64(epochNumber) - 1) * epoch)
|
||||
// if checkpointNumber < 0 {
|
||||
// checkpointNumber = 0
|
||||
// }
|
||||
// block, err = s.b.BlockByNumber(ctx, checkpointNumber)
|
||||
// if err != nil || block == nil {
|
||||
// return "", err
|
||||
// }
|
||||
//}
|
||||
blockNum := block.Number().Uint64()
|
||||
masternodes, err = s.GetMasternodes(ctx, block)
|
||||
if err != nil || len(masternodes) == 0 {
|
||||
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
|
||||
return "", err
|
||||
}
|
||||
for _, masternode := range masternodes {
|
||||
if coinbaseAddress == masternode {
|
||||
return statusMasternode, nil
|
||||
}
|
||||
}
|
||||
|
||||
// look up recent checkpoint headers to get penalty list
|
||||
for i := 0; i <= common.LimitPenaltyEpoch; i++ {
|
||||
if blockNum > uint64(i) * epoch {
|
||||
blockCheckpointNumber := rpc.BlockNumber(blockNum - (blockNum % epoch) - (uint64(i) * epoch))
|
||||
blockCheckpoint, err := s.b.BlockByNumber(ctx, blockCheckpointNumber)
|
||||
if err != nil {
|
||||
log.Error("Failed to get block by number", "num", blockCheckpointNumber, "err", err)
|
||||
continue
|
||||
}
|
||||
penalties = append(penalties, blockCheckpoint.Penalties()...)
|
||||
}
|
||||
}
|
||||
if len(penalties) > 0 {
|
||||
penaltyList = common.ExtractAddressFromBytes(penalties)
|
||||
for _, pen := range penaltyList {
|
||||
if coinbaseAddress == pen {
|
||||
return statusSlashed, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read smart contract to get candidate list
|
||||
client, err := s.b.GetIPCClient()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
addr := common.HexToAddress(common.MasternodeVotingSMC)
|
||||
validator, err := contractValidator.NewTomoValidator(addr, client)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
opts := new(bind.CallOpts)
|
||||
candidates, err = validator.GetCandidates(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// exclude masternodes
|
||||
proposedList = common.RemoveItemFromArray(candidates, masternodes)
|
||||
// exclude penalties
|
||||
proposedList = common.RemoveItemFromArray(proposedList, penaltyList)
|
||||
|
||||
for _, proposed := range proposedList {
|
||||
if coinbaseAddress == proposed {
|
||||
return statusProposed, nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -3715,6 +3715,13 @@ var inputBlockNumberFormatter = function (blockNumber) {
|
|||
return utils.toHex(blockNumber);
|
||||
};
|
||||
|
||||
var inputEpochNumberFormatter = function (epochNumber) {
|
||||
if (epochNumber === undefined || epochNumber === "latest") {
|
||||
return "latest";
|
||||
}
|
||||
return utils.toHex(epochNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats the input of a transaction and converts all values to HEX
|
||||
*
|
||||
|
|
@ -3966,6 +3973,7 @@ var outputSyncingFormatter = function(result) {
|
|||
module.exports = {
|
||||
inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter,
|
||||
inputBlockNumberFormatter: inputBlockNumberFormatter,
|
||||
inputEpochNumberFormatter: inputEpochNumberFormatter,
|
||||
inputCallFormatter: inputCallFormatter,
|
||||
inputTransactionFormatter: inputTransactionFormatter,
|
||||
inputAddressFormatter: inputAddressFormatter,
|
||||
|
|
@ -5472,6 +5480,12 @@ var methods = function () {
|
|||
params: 0
|
||||
});
|
||||
|
||||
var getCandidateStatus = new Method({
|
||||
name: 'getCandidateStatus',
|
||||
call: 'eth_getCandidateStatus',
|
||||
params: 2,
|
||||
inputFormatter: [formatters.inputAddressFormatter, formatters.inputEpochNumberFormatter]
|
||||
});
|
||||
return [
|
||||
getBalance,
|
||||
getStorageAt,
|
||||
|
|
@ -5479,6 +5493,7 @@ var methods = function () {
|
|||
getBlock,
|
||||
getBlockSigners,
|
||||
getBlockFinality,
|
||||
getCandidateStatus,
|
||||
getUncle,
|
||||
getCompilers,
|
||||
getBlockTransactionCount,
|
||||
|
|
|
|||
39
rpc/types.go
39
rpc/types.go
|
|
@ -117,11 +117,13 @@ type ServerCodec interface {
|
|||
}
|
||||
|
||||
type BlockNumber int64
|
||||
type EpochNumber int64
|
||||
|
||||
const (
|
||||
PendingBlockNumber = BlockNumber(-2)
|
||||
LatestBlockNumber = BlockNumber(-1)
|
||||
EarliestBlockNumber = BlockNumber(0)
|
||||
LatestEpochNumber = EpochNumber(-1)
|
||||
)
|
||||
|
||||
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
|
||||
|
|
@ -131,11 +133,7 @@ const (
|
|||
// - an invalid block number error when the given argument isn't a known strings
|
||||
// - an out of range error when the given block number is either too little or too large
|
||||
func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
input := strings.TrimSpace(string(data))
|
||||
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||
input = input[1 : len(input)-1]
|
||||
}
|
||||
|
||||
input := trimData(data)
|
||||
switch input {
|
||||
case "earliest":
|
||||
*bn = EarliestBlockNumber
|
||||
|
|
@ -163,3 +161,34 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
|||
func (bn BlockNumber) Int64() int64 {
|
||||
return (int64)(bn)
|
||||
}
|
||||
|
||||
func (e *EpochNumber) UnmarshalJSON(data []byte) error {
|
||||
input := trimData(data)
|
||||
if input == "latest" {
|
||||
*e = LatestEpochNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
eNum, err := hexutil.DecodeUint64(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if eNum > math.MaxInt64 {
|
||||
return fmt.Errorf("EpochNumber too high")
|
||||
}
|
||||
|
||||
*e = EpochNumber(eNum)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e EpochNumber) Int64() int64 {
|
||||
return (int64)(e)
|
||||
}
|
||||
|
||||
func trimData(data []byte) string {
|
||||
input := strings.TrimSpace(string(data))
|
||||
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||
input = input[1 : len(input)-1]
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue