Implement api eth.getCandidateStatus

This commit is contained in:
parmarrushabh 2019-02-14 14:59:37 +05:30
parent 56e1251a94
commit a479343cc7
4 changed files with 323 additions and 53 deletions

View file

@ -44,10 +44,17 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
) )
const ( const (
defaultGasPrice = 50 * params.Shannon defaultGasPrice = 50 * params.Shannon
// statuses of candidates
statusMasternode = "MASTERNODE"
statusSlashed = "SLASHED"
statusProposed = "PROPOSED"
) )
// PublicEthereumAPI provides an API to access Ethereum related information. // PublicEthereumAPI provides an API to access Ethereum related information.
@ -494,13 +501,7 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int {
// BlockNumber returns the block number of the chain head. // BlockNumber returns the block number of the chain head.
func (s *PublicBlockChainAPI) GetRewardByHash(hash common.Hash) map[string]interface{} { func (s *PublicBlockChainAPI) GetRewardByHash(hash common.Hash) map[string]interface{} {
if c, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { return s.b.GetRewardByHash(hash)
rewards := c.GetRewards(hash)
if rewards != nil {
return rewards
}
}
return make(map[string]interface{})
} }
// GetBalance returns the amount of wei for the given address in the state of the // GetBalance returns the amount of wei for the given address in the state of the
@ -614,6 +615,175 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
return res[:], state.Error() return res[:], state.Error()
} }
func (s *PublicBlockChainAPI) GetBlockSignersByHash(ctx context.Context, blockHash common.Hash) ([]common.Address, error) {
block, err := s.b.GetBlock(ctx, blockHash)
if err != nil || block == nil {
return []common.Address{}, err
}
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
}
return s.rpcOutputBlockSigners(block, ctx, masternodes)
}
func (s *PublicBlockChainAPI) GetBlockSignersByNumber(ctx context.Context, blockNumber rpc.BlockNumber) ([]common.Address, error) {
block, err := s.b.BlockByNumber(ctx, blockNumber)
if err != nil || block == nil {
return []common.Address{}, err
}
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
}
return s.rpcOutputBlockSigners(block, ctx, masternodes)
}
func (s *PublicBlockChainAPI) GetBlockFinalityByHash(ctx context.Context, blockHash common.Hash) (int32, error) {
block, err := s.b.GetBlock(ctx, blockHash)
if err != nil || block == nil {
return int32(0), err
}
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
}
blockSigners, err := s.rpcOutputBlockSigners(block, ctx, masternodes)
if err != nil {
return int32(0), err
}
return int32(100 * len(blockSigners) / len(masternodes)), err
}
func (s *PublicBlockChainAPI) GetBlockFinalityByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (int32, error) {
block, err := s.b.BlockByNumber(ctx, blockNumber)
if err != nil || block == nil {
return int32(0), err
}
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
}
blockSigners, err := s.rpcOutputBlockSigners(block, ctx, masternodes)
if err != nil {
return int32(0), err
}
return int32(100 * len(blockSigners) / len(masternodes)), err
}
// 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 {
curBlockNumber := b.Number().Uint64()
prevBlockNumber := curBlockNumber + (common.MergeSignRange - (curBlockNumber % common.MergeSignRange))
latestBlockNumber := s.b.CurrentBlock().Number().Uint64()
if prevBlockNumber >= latestBlockNumber || !s.b.ChainConfig().IsTIP2019(b.Number()) {
prevBlockNumber = curBlockNumber
}
if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
// Get block epoc latest.
lastCheckpointNumber := prevBlockNumber - (prevBlockNumber % s.b.ChainConfig().XDPoS.Epoch)
prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber))
if prevCheckpointBlock != nil {
masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().XDPoS.Epoch)
}
} else {
log.Error("Undefined XDPoS consensus engine")
}
}
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().XDPoS.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.NewXDCValidator(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. // CallArgs represents the arguments for a call.
type CallArgs struct { type CallArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
@ -860,46 +1030,55 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
uncleHashes[i] = uncle.Hash() uncleHashes[i] = uncle.Hash()
} }
fields["uncles"] = uncleHashes fields["uncles"] = uncleHashes
return fields, nil
}
func (s *PublicBlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context.Context, masternodes []common.Address) ([]common.Address, error) {
// Get signers for block. // Get signers for block.
client, err := s.b.GetIPCClient() client, err := s.b.GetIPCClient()
if err != nil { if err != nil {
log.Error("Fail to connect IPC client for block status", "error", err) log.Error("Fail to connect IPC client for block status", "error", err)
return []common.Address{}, err
} }
var signers []common.Address var signers []common.Address
var filterSigners []common.Address var filterSigners []common.Address
finality := int32(0)
if b.Number().Int64() > 0 { if b.Number().Int64() > 0 {
addrBlockSigner := common.HexToAddress(common.BlockSigners) curBlockNumber := b.Number().Uint64()
signers, err = contracts.GetSignersFromContract(addrBlockSigner, client, b.Hash()) prevBlockNumber := curBlockNumber + (common.MergeSignRange - (curBlockNumber % common.MergeSignRange))
if err != nil { latestBlockNumber := s.b.CurrentBlock().Number().Uint64()
log.Error("Fail to get signers from block signer SC.", "error", err) if prevBlockNumber >= latestBlockNumber || !s.b.ChainConfig().IsTIP2019(b.Number()) {
prevBlockNumber = curBlockNumber
} }
// Get block epoc latest. if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
if s.b.ChainConfig().XDPoS != nil { prevBlock, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(prevBlockNumber))
engine := s.b.GetEngine() if err != nil {
lastCheckpointNumber := rpc.BlockNumber(b.Number().Uint64() - (b.Number().Uint64() % s.b.ChainConfig().XDPoS.Epoch)) log.Error("Fail to get previous block", "error", err)
prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, lastCheckpointNumber) return []common.Address{}, err
if prevCheckpointBlock != nil { }
masternodes := engine.(*XDPoS.XDPoS).GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), b.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch) addrBlockSigner := common.HexToAddress(common.BlockSigners)
countFinality := 0 signers, err = contracts.GetSignersByExecutingEVM(addrBlockSigner, client, prevBlock.Hash())
for _, masternode := range masternodes { if err != nil {
for _, signer := range signers { log.Error("Fail to get signers from block signer SC.", "error", err)
if signer == masternode { return []common.Address{}, err
countFinality++ }
filterSigners = append(filterSigners, masternode) validator, _ := engine.RecoverValidator(b.Header())
break creator, _ := engine.RecoverSigner(b.Header())
} signers = append(signers, validator)
signers = append(signers, creator)
for _, masternode := range masternodes {
for _, signer := range signers {
if signer == masternode {
filterSigners = append(filterSigners, masternode)
break
} }
} }
finality = int32(countFinality * 100 / len(masternodes))
} }
} else {
log.Error("Undefined XDPoS consensus engine")
} }
} }
fields["signers"] = filterSigners return filterSigners, nil
fields["finality"] = finality
return fields, nil
} }
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
@ -1532,4 +1711,4 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint {
// Version returns the current ethereum protocol version. // Version returns the current ethereum protocol version.
func (s *PublicNetAPI) Version() string { func (s *PublicNetAPI) Version() string {
return fmt.Sprintf("%d", s.networkVersion) return fmt.Sprintf("%d", s.networkVersion)
} }

File diff suppressed because one or more lines are too long

View file

@ -3715,6 +3715,13 @@ var inputBlockNumberFormatter = function (blockNumber) {
return utils.toHex(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 * Formats the input of a transaction and converts all values to HEX
* *
@ -3840,6 +3847,22 @@ var outputBlockFormatter = function(block) {
return block; return block;
}; };
/**
* Formats the output of a blockSigner list
*
* @method outputBlockFormatter
* @param {Object} blockSigners
* @returns {Object}
*/
var outputBlockSignersFormatter = function(blockSigners) {
if (utils.isArray(blockSigners)) {
blockSigners.forEach(function(item){
if(!utils.isString(item))
return formatOutputAddress(item);
});
}
return blockSigners;
};
/** /**
* Formats the output of a log * Formats the output of a log
@ -3950,6 +3973,7 @@ var outputSyncingFormatter = function(result) {
module.exports = { module.exports = {
inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter, inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter,
inputBlockNumberFormatter: inputBlockNumberFormatter, inputBlockNumberFormatter: inputBlockNumberFormatter,
inputEpochNumberFormatter: inputEpochNumberFormatter,
inputCallFormatter: inputCallFormatter, inputCallFormatter: inputCallFormatter,
inputTransactionFormatter: inputTransactionFormatter, inputTransactionFormatter: inputTransactionFormatter,
inputAddressFormatter: inputAddressFormatter, inputAddressFormatter: inputAddressFormatter,
@ -3958,6 +3982,7 @@ module.exports = {
outputTransactionFormatter: outputTransactionFormatter, outputTransactionFormatter: outputTransactionFormatter,
outputTransactionReceiptFormatter: outputTransactionReceiptFormatter, outputTransactionReceiptFormatter: outputTransactionReceiptFormatter,
outputBlockFormatter: outputBlockFormatter, outputBlockFormatter: outputBlockFormatter,
outputBlockSignersFormatter: outputBlockSignersFormatter,
outputLogFormatter: outputLogFormatter, outputLogFormatter: outputLogFormatter,
outputPostFormatter: outputPostFormatter, outputPostFormatter: outputPostFormatter,
outputSyncingFormatter: outputSyncingFormatter outputSyncingFormatter: outputSyncingFormatter
@ -5210,6 +5235,14 @@ var blockCall = function (args) {
return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber";
}; };
var blockSignersCall = function (args) {
return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockSignersByHash" : "eth_getBlockSignersByNumber";
};
var blockFinalityCall = function (args) {
return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockFinalityByHash" : "eth_getBlockFinalityByNumber";
};
var transactionFromBlockCall = function (args) { var transactionFromBlockCall = function (args) {
return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex';
}; };
@ -5297,6 +5330,22 @@ var methods = function () {
outputFormatter: formatters.outputBlockFormatter outputFormatter: formatters.outputBlockFormatter
}); });
var getBlockSigners = new Method({
name: 'getBlockSigners',
call: blockSignersCall,
params: 1,
inputFormatter: [formatters.inputBlockNumberFormatter],
outputFormatter: formatters.outputBlockSignersFormatter
});
var getBlockFinality = new Method({
name: 'getBlockFinality',
call: blockFinalityCall,
params: 1,
inputFormatter: [formatters.inputBlockNumberFormatter],
outputFormatter: formatters.formatOutputInt
});
var getUncle = new Method({ var getUncle = new Method({
name: 'getUncle', name: 'getUncle',
call: uncleCall, call: uncleCall,
@ -5431,11 +5480,20 @@ var methods = function () {
params: 0 params: 0
}); });
var getCandidateStatus = new Method({
name: 'getCandidateStatus',
call: 'eth_getCandidateStatus',
params: 2,
inputFormatter: [formatters.inputAddressFormatter, formatters.inputEpochNumberFormatter]
});
return [ return [
getBalance, getBalance,
getStorageAt, getStorageAt,
getCode, getCode,
getBlock, getBlock,
getBlockSigners,
getBlockFinality,
getCandidateStatus,
getUncle, getUncle,
getCompilers, getCompilers,
getBlockTransactionCount, getBlockTransactionCount,

View file

@ -117,11 +117,13 @@ type ServerCodec interface {
} }
type BlockNumber int64 type BlockNumber int64
type EpochNumber int64
const ( const (
PendingBlockNumber = BlockNumber(-2) PendingBlockNumber = BlockNumber(-2)
LatestBlockNumber = BlockNumber(-1) LatestBlockNumber = BlockNumber(-1)
EarliestBlockNumber = BlockNumber(0) EarliestBlockNumber = BlockNumber(0)
LatestEpochNumber = EpochNumber(-1)
) )
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: // 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 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 // - an out of range error when the given block number is either too little or too large
func (bn *BlockNumber) UnmarshalJSON(data []byte) error { func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
input := strings.TrimSpace(string(data)) input := trimData(data)
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
input = input[1 : len(input)-1]
}
switch input { switch input {
case "earliest": case "earliest":
*bn = EarliestBlockNumber *bn = EarliestBlockNumber
@ -163,3 +161,34 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
func (bn BlockNumber) Int64() int64 { func (bn BlockNumber) Int64() int64 {
return (int64)(bn) 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
}