Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Web3Q validator set from PoS staking contract in remote chain #78

Merged
merged 16 commits into from
May 12, 2022
4 changes: 4 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ var (
utils.Web3QMainnetFlag,
utils.ValPortFlag,
utils.ValNodeKeyFlag,
utils.ValRpcKeyFlag,
utils.ValContractKeyFlag,
utils.ValChainIdKeyFlag,
utils.ValEnableEpockKeyFlag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.EthStatsURLFlag,
Expand Down
28 changes: 28 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ var (
Name: "validator.nodekey",
Usage: "Validator P2P node key file",
}
ValContractKeyFlag = cli.StringFlag{
Name: "validator.contract",
Usage: "the contract used by Validator to get latest validator set from",
}
ValChainIdKeyFlag = cli.StringFlag{
Name: "validator.chainid",
Usage: "the chain used by Validator to get latest validator set from",
}
ValEnableEpockKeyFlag = cli.StringFlag{
Name: "validator.enableepock",
Usage: "the epoch validator start to get validator set from contract, 0 means disable",
}
ValRpcKeyFlag = cli.StringFlag{
Name: "validator.rpc",
Usage: "rpc for Validator to update validator set",
}
DeveloperFlag = cli.BoolFlag{
Name: "dev",
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
Expand Down Expand Up @@ -1427,6 +1443,18 @@ func setTendermint(ctx *cli.Context, cfg *ethconfig.Config) {
if ctx.GlobalIsSet(ValNodeKeyFlag.Name) {
cfg.ValNodeKey = ctx.GlobalString(ValNodeKeyFlag.Name)
}
if ctx.GlobalIsSet(ValRpcKeyFlag.Name) {
cfg.ValRpc = ctx.GlobalString(ValRpcKeyFlag.Name)
}
if ctx.GlobalIsSet(ValEnableEpockKeyFlag.Name) {
cfg.ValEnableEpoch = ctx.GlobalUint64(ValEnableEpockKeyFlag.Name)
}
if ctx.GlobalIsSet(ValChainIdKeyFlag.Name) {
cfg.ValChainId = ctx.GlobalUint64(ValChainIdKeyFlag.Name)
}
if ctx.GlobalIsSet(ValContractKeyFlag.Name) {
cfg.ValContract = ctx.GlobalString(ValContractKeyFlag.Name)
}
}

func setMiner(ctx *cli.Context, cfg *miner.Config) {
Expand Down
130 changes: 107 additions & 23 deletions consensus/tendermint/gov/gov.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,53 @@
package gov

import (
"context"
"fmt"
"math"
"math/big"
"strings"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)

const (
validatorsetABI = `[{"inputs": [],"name": "GetEpochValidators","outputs": [{"internalType": "uint256","name": "EpochIdx","type": "uint256"},{"internalType": "address[]","name": "Validators","type": "address[]"},{"internalType": "uint256[]","name": "Powers","type": "uint256[]"}],"stateMutability": "view","type": "function"},{"inputs": [],"name": "proposalValidators","outputs": [{"internalType": "address[]","name": "Validators","type": "address[]"},{"internalType": "uint256[]","name": "Powers","type": "uint256[]"}],"stateMutability": "view","type": "function"}]`
confirmedNumber = 96
contractFunc_GetValidator = "proposalValidators"
gas = uint64(math.MaxUint64 / 2)
)

type Governance struct {
config *params.TendermintConfig
chain consensus.ChainHeaderReader
ctx context.Context
config *params.TendermintConfig
chain consensus.ChainHeaderReader
validatorSetABI abi.ABI
client *ethclient.Client
contract *common.Address
}

func New(config *params.TendermintConfig, chain consensus.ChainHeaderReader) *Governance {
return &Governance{config: config, chain: chain}
func New(config *params.TendermintConfig, chain consensus.ChainHeaderReader, client *ethclient.Client) *Governance {
vABI, _ := abi.JSON(strings.NewReader(validatorsetABI))
contract := common.HexToAddress(config.ValidatorContract)
return &Governance{
ctx: context.Background(),
config: config,
chain: chain,
client: client,
validatorSetABI: vABI,
contract: &contract,
}
}

// Returns the validator sets for last, current blocks
// GetValidatorSets Returns the validator sets for last, current blocks
func (g *Governance) GetValidatorSets(height uint64) (*types.ValidatorSet, *types.ValidatorSet) {
if height == 0 {
panic("cannot get genesis validator set")
Expand All @@ -28,7 +59,6 @@ func (g *Governance) GetValidatorSets(height uint64) (*types.ValidatorSet, *type
}

// GetValidatorSet returns the validator set of a height

func (g *Governance) GetValidatorSet(height uint64, lastVals *types.ValidatorSet) *types.ValidatorSet {
if height == 0 {
return &types.ValidatorSet{}
Expand All @@ -53,19 +83,46 @@ func (g *Governance) GetValidatorSet(height uint64, lastVals *types.ValidatorSet
return epochVals
}

func (g *Governance) NextValidators(height uint64) []common.Address {
func (g *Governance) NextValidatorsAndPowers(height uint64, remoteChainNumber uint64) ([]common.Address, []uint64, uint64, error) {
if height%g.config.Epoch != 0 {
return []common.Address{}
return []common.Address{}, []uint64{}, 0, nil
}

log.Info("new epoch", "epoch", height/g.config.Epoch)
switch {
case height == 0:
header := g.chain.GetHeaderByNumber(0)
return header.NextValidators
return header.NextValidators, header.NextValidatorPowers, 0, nil
default:
// TODO: get real validators by calling contract, currently use genesis
header := g.chain.GetHeaderByNumber(0)
return header.NextValidators
epochId := height / g.config.Epoch
if g.client == nil || g.config.EnableEpock > epochId {
ping-ke marked this conversation as resolved.
Show resolved Hide resolved
header := g.chain.GetHeaderByNumber(0)
return header.NextValidators, header.NextValidatorPowers, 0, nil
}

number, err := g.client.BlockNumber(g.ctx)
if err != nil {
return nil, nil, 0, err
}

if remoteChainNumber == 0 {
if number <= confirmedNumber {
return nil, nil, 0, fmt.Errorf("remote chain number %d smaller than confirmedNumber %d", number, confirmedNumber)
}
remoteChainNumber = number - confirmedNumber
} else if remoteChainNumber == uint64(math.MaxUint64) {
return nil, nil, remoteChainNumber, fmt.Errorf("parse remoteChainNumber")
} else {
if number-confirmedNumber/2*3 > remoteChainNumber || number-confirmedNumber/2 < remoteChainNumber {
return nil, nil, 0, fmt.Errorf("remoteChainNumber %d is out of range [%d, %d]",
remoteChainNumber, number-confirmedNumber/2*3, number-confirmedNumber/2)
}
}

validators, powers, err := g.GetValidatorsAndPowersFromContract(remoteChainNumber)

log.Info("get validators and powers", "validators", validators, "powers", powers)
return validators, powers, remoteChainNumber, err
}
}

Expand Down Expand Up @@ -97,18 +154,45 @@ func CompareValidatorPowers(lhs, rhs []uint64) bool {
return true
}

func (g *Governance) NextValidatorPowers(height uint64) []uint64 {
if height%g.config.Epoch != 0 {
return []uint64{}
// GetValidatorsAndPowersFromContract get current validators
func (g *Governance) GetValidatorsAndPowersFromContract(blockNumber uint64) ([]common.Address, []uint64, error) {
data, err := g.validatorSetABI.Pack(contractFunc_GetValidator)
if err != nil {
return nil, nil, err
}

switch {
case height == 0:
header := g.chain.GetHeaderByNumber(0)
return header.NextValidatorPowers
default:
// TODO get real validators by calling contract
header := g.chain.GetHeaderByNumber(0)
return header.NextValidatorPowers
// call
msgData := (hexutil.Bytes)(data)
msg := ethereum.CallMsg{
To: g.contract,
Gas: gas,
Data: msgData,
}
result, err := g.client.CallContract(g.ctx, msg, new(big.Int).SetUint64(blockNumber))
if err != nil {
return nil, nil, err
}

type validators struct {
Validators []common.Address
Powers []*big.Int
}

var v validators

if err := g.validatorSetABI.UnpackIntoInterface(&v, contractFunc_GetValidator, result); err != nil {
return nil, nil, err
}

if len(v.Validators) != len(v.Powers) {
return nil, nil, fmt.Errorf("invalid validator set: validator count %d is mismatch with power count %d",
len(v.Validators), len(v.Powers))
}

powers := make([]uint64, len(v.Powers))
for i, p := range v.Powers {
powers[i] = p.Uint64()
}

return v.Validators, powers, nil
}
Loading