diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fac09625b289..7374123d2f41 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -151,6 +151,10 @@ var ( utils.Web3QMainnetFlag, utils.ValPortFlag, utils.ValNodeKeyFlag, + utils.ValRpcFlag, + utils.ValContractFlag, + utils.ValChainIdFlag, + utils.ValChangeEpochIdFlag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 412bb8bf4c17..432abab9f12b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -181,6 +181,22 @@ var ( Name: "validator.nodekey", Usage: "Validator P2P node key file", } + ValContractFlag = cli.StringFlag{ + Name: "validator.contract", + Usage: "the contract used by Validator to get latest validator set from", + } + ValChainIdFlag = cli.StringFlag{ + Name: "validator.chainid", + Usage: "the chain used by Validator to get latest validator set from", + } + ValChangeEpochIdFlag = cli.StringFlag{ + Name: "validator.changeepochid", + Usage: "the epoch validator start to get validator set from contract, 0 means disable", + } + ValRpcFlag = 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", @@ -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(ValRpcFlag.Name) { + cfg.ValRpc = ctx.GlobalString(ValRpcFlag.Name) + } + if ctx.GlobalIsSet(ValChangeEpochIdFlag.Name) { + cfg.ValidatorChangeEpochId = ctx.GlobalUint64(ValChangeEpochIdFlag.Name) + } + if ctx.GlobalIsSet(ValChainIdFlag.Name) { + cfg.ValChainId = ctx.GlobalUint64(ValChainIdFlag.Name) + } + if ctx.GlobalIsSet(ValContractFlag.Name) { + cfg.ValContract = ctx.GlobalString(ValContractFlag.Name) + } } func setMiner(ctx *cli.Context, cfg *miner.Config) { diff --git a/consensus/tendermint/adapter/store.go b/consensus/tendermint/adapter/store.go index cc5b55b78900..1c8b3dc6dd4b 100644 --- a/consensus/tendermint/adapter/store.go +++ b/consensus/tendermint/adapter/store.go @@ -1,32 +1,41 @@ package adapter import ( + "bytes" "context" + "encoding/binary" "errors" "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" pbft "github.com/ethereum/go-ethereum/consensus/tendermint/consensus" + "github.com/ethereum/go-ethereum/consensus/tendermint/gov" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" ) +var prefix = []byte("HeaderNumber") + type Store struct { + config *params.TendermintConfig chain *core.BlockChain verifyHeaderFunc func(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error makeBlock func(parentHash common.Hash, coinbase common.Address, timestamp uint64) (block *types.Block, err error) + gov *gov.Governance mux *event.TypeMux } func NewStore( + config *params.TendermintConfig, chain *core.BlockChain, verifyHeaderFunc func(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error, makeBlock func(parentHash common.Hash, coinbase common.Address, timestamp uint64) (block *types.Block, err error), + gov *gov.Governance, mux *event.TypeMux) *Store { - return &Store{chain: chain, verifyHeaderFunc: verifyHeaderFunc, makeBlock: makeBlock, mux: mux} + return &Store{config: config, chain: chain, verifyHeaderFunc: verifyHeaderFunc, makeBlock: makeBlock, gov: gov, mux: mux} } func (s *Store) Base() uint64 { @@ -82,11 +91,47 @@ func (s *Store) SaveBlock(block *types.FullBlock, commit *types.Commit) { // Validate a block without Commit and with LastCommit. func (s *Store) ValidateBlock(state pbft.ChainState, block *types.FullBlock) (err error) { - err = s.verifyHeaderFunc(s.chain, block.Header(), false) + header := block.Header() + err = s.verifyHeaderFunc(s.chain, header, false) if err != nil { return } + validators, powers := []common.Address{}, []uint64{} + if header.Number.Uint64()%s.config.Epoch == 0 { + epochId := header.Number.Uint64() / s.config.Epoch + remoteChainNumber := uint64(0) + hash, emptyHash := common.Hash{}, common.Hash{} + if s.config.ValidatorChangeEpochId > 0 && s.config.ValidatorChangeEpochId <= epochId { + l := len(prefix) + if len(header.Extra) >= l+8+32 && bytes.Compare(header.Extra[:l], prefix) == 0 { + nb := header.Extra[l : l+8] + remoteChainNumber = binary.BigEndian.Uint64(nb) + if remoteChainNumber == 0 { + return errors.New("invalid remoteChainNumber in header.Extra") + } + hb := header.Extra[l+8 : l+8+32] + hash = common.BytesToHash(hb) + if hash == emptyHash { + return errors.New("invalid remote block hash in header.Extra") + } + } else { + return errors.New("header.Extra missing validator chain block height and hash") + } + } + + validators, powers, err = s.gov.NextValidatorsAndPowersAt(epochId, remoteChainNumber, hash) + if err != nil { + return errors.New(fmt.Sprintf("verifyHeader failed with %s", err.Error())) + } + } + if !gov.CompareValidators(header.NextValidators, validators) { + return errors.New("NextValidators is incorrect") + } + if !gov.CompareValidatorPowers(header.NextValidatorPowers, powers) { + return errors.New("NextValidatorPowers is incorrect") + } + // Validate if the block matches current state. if state.LastBlockHeight == 0 && block.NumberU64() != state.InitialHeight { return fmt.Errorf("wrong Block.Header.Height. Expected %v for initial block, got %v", @@ -235,6 +280,33 @@ func (s *Store) MakeBlock( header.TimeMs = timestampMs header.LastCommitHash = commit.Hash() + if height%s.config.Epoch == 0 { + epochId := height / s.config.Epoch + remoteChainNumber := uint64(0) + hash := common.Hash{} + + header.NextValidators, header.NextValidatorPowers, remoteChainNumber, hash, err = + s.gov.NextValidatorsAndPowersForProposal(epochId) + if err != nil { + log.Error(err.Error()) + return nil + } + + // remoteChainNumber == 0 when NextValidatorsAndPowers return err or + // when update validator from contract is not enable. as err has been handled above. + // so only when remoteChainNumber != 0 need to add number and hash to header.Extra. + if remoteChainNumber != 0 { + data := prefix + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, remoteChainNumber) + data = append(data, b...) + data = append(data, hash.Bytes()...) + header.Extra = append(data, header.Extra...) + } + } else { + header.NextValidators, header.NextValidatorPowers = []common.Address{}, []uint64{} + } + block = block.WithSeal(header) return &types.FullBlock{Block: block, LastCommit: commit} diff --git a/consensus/tendermint/gov/gov.go b/consensus/tendermint/gov/gov.go index 230f2f91ebc4..3abd3a58e971 100644 --- a/consensus/tendermint/gov/gov.go +++ b/consensus/tendermint/gov/gov.go @@ -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": "proposedValidators","outputs": [{"internalType": "address[]","name": "Validators","type": "address[]"},{"internalType": "uint256[]","name": "Powers","type": "uint256[]"}],"stateMutability": "view","type": "function"}]` + confirmedNumber = 96 + contractFunc_GetValidator = "proposedValidators" + 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") @@ -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{} @@ -53,20 +83,116 @@ func (g *Governance) GetValidatorSet(height uint64, lastVals *types.ValidatorSet return epochVals } -func (g *Governance) NextValidators(height uint64) []common.Address { - if height%g.config.Epoch != 0 { - return []common.Address{} +// NextValidatorsAndPowersForProposal get next validators according to block height and config +func (g *Governance) NextValidatorsAndPowersForProposal(epochId uint64) ([]common.Address, []uint64, uint64, common.Hash, error) { + log.Debug("NextValidatorsAndPowersForProposal", "epoch", epochId) + if g.config.ValidatorChangeEpochId == 0 || g.config.ValidatorChangeEpochId > epochId { + header := g.chain.GetHeaderByNumber(0) + return header.NextValidators, header.NextValidatorPowers, 0, common.Hash{}, nil } - switch { - case height == 0: - header := g.chain.GetHeaderByNumber(0) - return header.NextValidators - default: - // TODO: get real validators by calling contract, currently use genesis + number, err := g.client.BlockNumber(g.ctx) + if err != nil { + return nil, nil, 0, common.Hash{}, err + } + + if number <= confirmedNumber { + return nil, nil, 0, common.Hash{}, fmt.Errorf( + "remote chain number %d smaller than confirmedNumber %d", number, confirmedNumber) + } + number = number - confirmedNumber + + header, err := g.client.BlockByNumber(g.ctx, new(big.Int).SetUint64(number)) + if err != nil { + return nil, nil, 0, common.Hash{}, err + } + + validators, powers, err := g.getValidatorsAndPowersFromContract(header.Hash()) + if err != nil { + return nil, nil, 0, common.Hash{}, err + } + + log.Debug("get validators and powers", "validators", validators, "powers", powers) + return validators, powers, number, header.Hash(), err +} + +// NextValidatorsAndPowersAt get next validators according to block height and config +func (g *Governance) NextValidatorsAndPowersAt(epochId uint64, remoteChainNumber uint64, hash common.Hash) ([]common.Address, []uint64, error) { + log.Debug("NextValidatorsAndPowersAt", "epoch", epochId) + if g.config.ValidatorChangeEpochId == 0 || g.config.ValidatorChangeEpochId > epochId { header := g.chain.GetHeaderByNumber(0) - return header.NextValidators + return header.NextValidators, header.NextValidatorPowers, nil + } + + number, err := g.client.BlockNumber(g.ctx) + if err != nil { + return nil, nil, err + } + + if number-confirmedNumber/2*3 > remoteChainNumber || number-confirmedNumber/2 < remoteChainNumber { + return nil, nil, fmt.Errorf("remoteChainNumber %d is out of range [%d, %d]", + remoteChainNumber, number-confirmedNumber/2*3, number-confirmedNumber/2) + } + + header, err := g.client.BlockByNumber(g.ctx, new(big.Int).SetUint64(remoteChainNumber)) + if err != nil { + return nil, nil, err + } + + if hash != header.Hash() { + fmt.Errorf("block hash mismatch", "remoteChainNumber hash", header.Hash(), "hash", hash) + } + + validators, powers, err := g.getValidatorsAndPowersFromContract(hash) + if err != nil { + return nil, nil, err } + + log.Debug("get validators and powers", "validators", validators, "powers", powers) + return validators, powers, err +} + +// getValidatorsAndPowersFromContract get next validators from contract +func (g *Governance) getValidatorsAndPowersFromContract(blockHash common.Hash) ([]common.Address, []uint64, error) { + data, err := g.validatorSetABI.Pack(contractFunc_GetValidator) + if err != nil { + return nil, nil, err + } + + // call + msgData := (hexutil.Bytes)(data) + msg := ethereum.CallMsg{ + To: g.contract, + Gas: gas, + Data: msgData, + } + result, err := g.client.CallContractAtHash(g.ctx, msg, blockHash) + 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 } func CompareValidators(lhs, rhs []common.Address) bool { @@ -96,19 +222,3 @@ func CompareValidatorPowers(lhs, rhs []uint64) bool { return true } - -func (g *Governance) NextValidatorPowers(height uint64) []uint64 { - if height%g.config.Epoch != 0 { - return []uint64{} - } - - 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 - } -} diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go index 93a9d7af2a68..98b0944fc637 100644 --- a/consensus/tendermint/tendermint.go +++ b/consensus/tendermint/tendermint.go @@ -32,12 +32,13 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/tendermint/adapter" - pbftconsensus "github.com/ethereum/go-ethereum/consensus/tendermint/consensus" + pbft "github.com/ethereum/go-ethereum/consensus/tendermint/consensus" "github.com/ethereum/go-ethereum/consensus/tendermint/gov" libp2p "github.com/ethereum/go-ethereum/consensus/tendermint/p2p" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -54,7 +55,6 @@ var ( nonceDefault = types.BlockNonce{} // Default nonce number. uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. - ) // Various error messages to mark blocks invalid. These should be private to @@ -88,28 +88,68 @@ type Tendermint struct { rootCtx context.Context lock sync.RWMutex // Protects the signer fields - privVal pbftconsensus.PrivValidator + privVal pbft.PrivValidator p2pserver *libp2p.Server + client *ethclient.Client } // New creates a Clique proof-of-authority consensus engine with the initial // signers set to the ones provided by the user. -func New(config *params.TendermintConfig) *Tendermint { +func New(config *params.TendermintConfig) (*Tendermint, error) { // Set any missing consensus parameters to their defaults conf := *config if conf.Epoch == 0 { conf.Epoch = epochLength } - return &Tendermint{ - config: &conf, + // Node's main lifecycle context. + rootCtx, rootCtxCancel := context.WithCancel(context.Background()) + + if conf.ValidatorChangeEpochId == 0 { // ValidatorChangeEpochId == 0 means disable update validator set + return &Tendermint{ + config: &conf, + rootCtx: rootCtx, + rootCtxCancel: rootCtxCancel, + }, nil + } + + if conf.ContractChainID == 0 { + return nil, errors.New("TendermintConfig err: ContractChainID is required when ValidatorChangeEpochId is not 0") + } + + if conf.ValidatorContract == "" { + return nil, errors.New("TendermintConfig err: ValidatorContract is required when ValidatorChangeEpochId is not 0") + } + + client, err := ethclient.Dial(conf.ValRpc) + if err != nil { + return nil, err } + + cId, err := client.ChainID(rootCtx) + if err != nil { + return nil, err + } + + if conf.ContractChainID != cId.Uint64() { + return nil, fmt.Errorf("ContractChainID is set to %d, but chainId using by rpc %s is %d", + conf.ContractChainID, conf.ValRpc, cId) + } + + return &Tendermint{ + config: &conf, + rootCtx: rootCtx, + rootCtxCancel: rootCtxCancel, + client: client, + }, nil } // SignerFn hashes and signs the data to be signed by a backing account. type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) +type SignTxFn func(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) + // Authorize injects a private key into the consensus engine to mint new blocks // with. func (c *Tendermint) Authorize(signer common.Address, signFn SignerFn) { @@ -120,7 +160,7 @@ func (c *Tendermint) Authorize(signer common.Address, signFn SignerFn) { c.privVal = NewEthPrivValidator(signer, signFn) } -func (c *Tendermint) getPrivValidator() pbftconsensus.PrivValidator { +func (c *Tendermint) getPrivValidator() pbft.PrivValidator { c.lock.Lock() defer c.lock.Unlock() @@ -133,18 +173,14 @@ func (c *Tendermint) P2pServer() *libp2p.Server { func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error), mux *event.TypeMux) (err error) { // Outbound gossip message queue - sendC := make(chan pbftconsensus.Message, 1000) + sendC := make(chan pbft.Message, 1000) // Inbound observations - obsvC := make(chan pbftconsensus.MsgInfo, 1000) - - // Node's main lifecycle context. - rootCtx, rootCtxCancel := context.WithCancel(context.Background()) - c.rootCtxCancel = rootCtxCancel - c.rootCtx = rootCtx + obsvC := make(chan pbft.MsgInfo, 1000) + gov := gov.New(c.config, chain, c.client) // datastore - store := adapter.NewStore(chain, c.VerifyHeader, makeBlock, mux) + store := adapter.NewStore(c.config, chain, c.VerifyHeader, makeBlock, gov, mux) // p2p key if TestMode { @@ -157,7 +193,7 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H } // p2p server - p2pserver, err := libp2p.NewP2PServer(rootCtx, store, obsvC, sendC, p2pPriv, c.config.P2pPort, c.config.NetworkID, c.config.P2pBootstrap, c.config.NodeName, rootCtxCancel) + p2pserver, err := libp2p.NewP2PServer(c.rootCtx, store, obsvC, sendC, p2pPriv, c.config.P2pPort, c.config.NetworkID, c.config.P2pBootstrap, c.config.NodeName, c.rootCtxCancel) if err != nil { return } @@ -165,19 +201,17 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H c.p2pserver = p2pserver go func() { - err := p2pserver.Run(rootCtx) + err := p2pserver.Run(c.rootCtx) if err != nil { log.Warn("p2pserver.Run", "err", err) } }() - gov := gov.New(c.config, chain) - block := chain.CurrentHeader() number := block.Number.Uint64() last, current := gov.GetValidatorSets(number + 1) - gcs := pbftconsensus.MakeChainState( + gcs := pbft.MakeChainState( c.config.NetworkID, number, block.Hash(), @@ -188,8 +222,8 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H ) // consensus - consensusState := pbftconsensus.NewConsensusState( - rootCtx, + consensusState := pbft.NewConsensusState( + c.rootCtx, &c.config.ConsensusConfig, *gcs, store, @@ -201,14 +235,14 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H privVal := c.getPrivValidator() if privVal != nil { consensusState.SetPrivValidator(privVal) - pubkey, err := privVal.GetPubKey(rootCtx) + pubkey, err := privVal.GetPubKey(c.rootCtx) if err != nil { panic("fail to get validator address") } log.Info("Chamber consensus in validator mode", "validator_addr", pubkey.Address()) } - err = consensusState.Start(rootCtx) + err = consensusState.Start(c.rootCtx) if err != nil { log.Warn("consensusState.Start", "err", err) } @@ -336,12 +370,8 @@ func (c *Tendermint) verifyHeader(chain consensus.ChainHeaderReader, header *typ return consensus.ErrFutureBlock } - governance := gov.New(c.config, chain) - if !gov.CompareValidators(header.NextValidators, governance.NextValidators(number)) { - return errors.New("NextValidators is incorrect") - } - if !gov.CompareValidatorPowers(header.NextValidatorPowers, governance.NextValidatorPowers(number)) { - return errors.New("NextValidatorPowers is incorrect") + if number%c.config.Epoch != 0 && len(header.NextValidators) != 0 { + return errors.New("NextValidators must be empty if number%c.config.Epoch != 0") } if len(header.NextValidatorPowers) != len(header.NextValidators) { return errors.New("NextValidators must have the same len as powers") @@ -439,22 +469,14 @@ func (c *Tendermint) VerifyUncles(chain consensus.ChainReader, block *types.Bloc // Prepare implements consensus.Engine, preparing all the consensus fields of the // header for running the transactions on top. // This method should be called by store.MakeBlock() -> worker.getSealingBlock() -> engine.Prepare(). -func (c *Tendermint) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - number := header.Number.Uint64() - +func (c *Tendermint) Prepare(chain consensus.ChainHeaderReader, header *types.Header) (err error) { header.Difficulty = big.NewInt(1) // Use constant nonce at the monent header.Nonce = nonceDefault // Mix digest is reserved for now, set to empty header.MixDigest = common.Hash{} - // Timestamp should be already set in store.MakeBlock() - - governance := gov.New(c.config, chain) - header.NextValidators = governance.NextValidators(number) - header.NextValidatorPowers = governance.NextValidatorPowers(number) - - return nil + return } // Finalize implements consensus.Engine, ensuring no uncles are set, nor block diff --git a/eth/backend.go b/eth/backend.go index ada120231c4f..354526993393 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -155,6 +155,18 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.ValNodeKey != "" { chainConfig.Tendermint.NodeKeyPath = config.ValNodeKey } + if config.ValContract != "" { + chainConfig.Tendermint.ValidatorContract = config.ValContract + } + if config.ValChainId != 0 { + chainConfig.Tendermint.ContractChainID = config.ValChainId + } + if config.ValidatorChangeEpochId != 0 { + chainConfig.Tendermint.ValidatorChangeEpochId = config.ValidatorChangeEpochId + } + if config.ValRpc != "" { + chainConfig.Tendermint.ValRpc = config.ValRpc + } } log.Info("Initialised chain configuration", "config", chainConfig) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 4729a420c67c..16e6ef337421 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -211,19 +211,32 @@ type Config struct { OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"` // Validator config - ValP2pPort uint - ValNodeKey string + ValP2pPort uint + ValNodeKey string + ValRpc string + ValContract string + ValChainId uint64 + ValidatorChangeEpochId uint64 } // CreateConsensusEngine creates a consensus engine for the given chain configuration. -func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine( + stack *node.Node, + chainConfig *params.ChainConfig, + config *ethash.Config, + notify []string, + noverify bool, + db ethdb.Database) consensus.Engine { // If proof-of-authority is requested, set it up var engine consensus.Engine if chainConfig.Clique != nil { engine = clique.New(chainConfig.Clique, db) } else if chainConfig.Tendermint != nil { - engine = tendermint.New(chainConfig.Tendermint) - return engine + var err error + engine, err = tendermint.New(chainConfig.Tendermint) + if err != nil { + panic(err.Error()) + } } else { switch config.PowMode { case ethash.ModeFake: diff --git a/params/config.go b/params/config.go index 8a0328f6309f..b6731ee4096d 100644 --- a/params/config.go +++ b/params/config.go @@ -304,11 +304,15 @@ var ( LondonBlock: big.NewInt(0), ArrowGlacierBlock: nil, Tendermint: &TendermintConfig{ - Epoch: 100800, // expect 6s block interval = one week - P2pPort: 33333, - ProposerRepetition: 8, - P2pBootstrap: strings.Join(Web3QGalileoValBootnodes, ","), - NodeKeyPath: "", + Epoch: 100800, // expect 6s block interval = one week + ValidatorContract: "", + ContractChainID: 0, + ValidatorChangeEpochId: 0, + ValRpc: "", + P2pPort: 33333, + ProposerRepetition: 8, + P2pBootstrap: strings.Join(Web3QGalileoValBootnodes, ","), + NodeKeyPath: "", ConsensusConfig: ConsensusConfig{ // WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"), TimeoutPropose: 3000 * time.Millisecond, @@ -496,14 +500,18 @@ type CliqueConfig struct { } type TendermintConfig struct { - Epoch uint64 `json:"epoch"` // Epoch lengh to vote new validator - NodeKeyPath string - P2pPort uint - NetworkID string - P2pBootstrap string - NodeName string - ProposerRepetition uint64 - ConsensusConfig ConsensusConfig + Epoch uint64 `json:"epoch"` // Epoch lengh to vote new validator + ValidatorContract string `json:"validatorContract"` // Validator set contract + ContractChainID uint64 `json:"contractChainId"` // Chain ID which Validator contract on + ValidatorChangeEpochId uint64 `json:"valChangeEpochId"` // Epoch to enable update ValidatorSet from contract + ValRpc string `json:"valRpc"` // rpc for ethclient to get ValidatorSet from contract + NodeKeyPath string + P2pPort uint + NetworkID string + P2pBootstrap string + NodeName string + ProposerRepetition uint64 + ConsensusConfig ConsensusConfig } // String implements the stringer interface, returning the consensus engine details.