Skip to content

Commit

Permalink
Merge pull request ethereum#35 from QuarkChain/tm_w3q_multi_val
Browse files Browse the repository at this point in the history
integrate eth wallet for chamber validator
  • Loading branch information
qizhou authored Mar 8, 2022
2 parents e48ff52 + a42b214 commit 5c9a842
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 23 deletions.
3 changes: 3 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ func prepare(ctx *cli.Context) {
case ctx.GlobalIsSet(utils.Web3QTestnetFlag.Name):
log.Info("Starting Geth in Web3Q dev mode...")

case ctx.GlobalIsSet(utils.Web3QGalileoFlag.Name):
log.Info("Starting Geth in Web3Q Galileo testnet...")

case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
log.Info("Starting Geth on Ethereum mainnet...")
}
Expand Down
73 changes: 73 additions & 0 deletions consensus/tendermint/priv_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package tendermint

import (
"context"

pbft "github.com/QuarkChain/go-minimal-pbft/consensus"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

type EthPrivValidator struct {
signer common.Address // Ethereum address of the signing key
signFn SignerFn // Signer function to authorize hashes with
}

type EthPubKey struct {
signer common.Address
}

func (pubkey *EthPubKey) Type() string {
return "ETH_PUBKEY"
}

func (pubkey *EthPubKey) Address() common.Address {
return pubkey.signer
}

func (pubkey *EthPubKey) VerifySignature(msg []byte, sig []byte) bool {
pub, err := crypto.Ecrecover(msg, sig)
if err != nil {
return false
}

if len(pub) == 0 || pub[0] != 4 {
return false
}

var signer common.Address
copy(signer[:], crypto.Keccak256(pub[1:])[12:])
return signer == pubkey.signer
}

func NewEthPrivValidator(signer common.Address, signFn SignerFn) pbft.PrivValidator {
return &EthPrivValidator{signer: signer, signFn: signFn}
}

func (pv *EthPrivValidator) Address() common.Address {
return pv.signer
}

func (pv *EthPrivValidator) GetPubKey(context.Context) (pbft.PubKey, error) {
return &EthPubKey{signer: pv.signer}, nil
}

func (pv *EthPrivValidator) SignVote(ctx context.Context, chainId string, vote *pbft.Vote) error {
vote.TimestampMs = uint64(pbft.CanonicalNowMs())
b := vote.VoteSignBytes(chainId)

sign, err := pv.signFn(accounts.Account{Address: pv.signer}, accounts.MimetypeClique, b)
vote.Signature = sign

return err
}

func (pv *EthPrivValidator) SignProposal(ctx context.Context, chainID string, proposal *pbft.Proposal) error {
// TODO: sanity check
b := proposal.ProposalSignBytes(chainID)

sign, err := pv.signFn(accounts.Account{Address: pv.signer}, accounts.MimetypeClique, b)
proposal.Signature = sign
return err
}
47 changes: 35 additions & 12 deletions consensus/tendermint/tendermint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ import (
"fmt"
"io/ioutil"
"math/big"
"sync"
"time"

pbftconsensus "github.com/QuarkChain/go-minimal-pbft/consensus"
libp2p "github.com/QuarkChain/go-minimal-pbft/p2p"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
Expand Down Expand Up @@ -85,6 +87,9 @@ type Tendermint struct {
config *params.TendermintConfig // Consensus engine configuration parameters
rootCtxCancel context.CancelFunc
rootCtx context.Context

lock sync.RWMutex // Protects the signer fields
privVal pbftconsensus.PrivValidator
}

// New creates a Clique proof-of-authority consensus engine with the initial
Expand All @@ -101,6 +106,25 @@ func New(config *params.TendermintConfig) *Tendermint {
}
}

// 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)

// Authorize injects a private key into the consensus engine to mint new blocks
// with.
func (c *Tendermint) Authorize(signer common.Address, signFn SignerFn) {
c.lock.Lock()
defer c.lock.Unlock()

c.privVal = NewEthPrivValidator(signer, signFn)
}

func (c *Tendermint) getPrivValidator() pbftconsensus.PrivValidator {
c.lock.Lock()
defer c.lock.Unlock()

return c.privVal
}

func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error)) (err error) {
// Outbound gossip message queue
sendC := make(chan pbftconsensus.Message, 1000)
Expand Down Expand Up @@ -133,15 +157,6 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H
// datastore
store := adapter.NewStore(chain, c.VerifyHeader, makeFullBlock)

// validator key
valKey, err := loadValidatorKey(c.config.ValKeyPath)
if err != nil {
return
}

var privVal pbftconsensus.PrivValidator
privVal = pbftconsensus.NewPrivValidatorLocal(valKey)

// p2p key
p2pPriv, err := loadP2pKey(c.config.NodeKeyPath)
if err != nil {
Expand Down Expand Up @@ -196,7 +211,15 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H
sendC,
)

consensusState.SetPrivValidator(privVal)
privVal := c.getPrivValidator()
if privVal != nil {
consensusState.SetPrivValidator(privVal)
pubkey, err := privVal.GetPubKey(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)
if err != nil {
Expand Down Expand Up @@ -420,7 +443,7 @@ func (c *Tendermint) Seal(chain consensus.ChainHeaderReader, block *types.Block,
// * DIFF_INTURN(1) if BLOCK_NUMBER % SIGNER_COUNT == SIGNER_INDEX
func (c *Tendermint) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
// TOOD: no diff is required
return big.NewInt(0)
return big.NewInt(1)
}

// SealHash returns the hash of a block prior to it being sealed.
Expand All @@ -440,5 +463,5 @@ func (c *Tendermint) Close() error {
// APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
func (c *Tendermint) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return []rpc.API{{}}
return []rpc.API{}
}
22 changes: 20 additions & 2 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/tendermint"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
Expand Down Expand Up @@ -473,14 +474,31 @@ func (s *Ethereum) StartMining(threads int) error {
cli = c
}
}
if cli != nil {
var tm *tendermint.Tendermint
if c, ok := s.engine.(*tendermint.Tendermint); ok {
tm = c
}
if cli != nil || tm != nil {
wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "err", err)
return fmt.Errorf("signer missing: %v", err)
}
cli.Authorize(eb, wallet.SignData)
if cli != nil {
cli.Authorize(eb, wallet.SignData)
}
if tm != nil {
tm.Authorize(eb, wallet.SignData)

err := tm.Init(s.blockchain, func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error) {
return s.miner.GetSealingBlock(parent, timestamp, coinbase, common.Hash{})
})
if err != nil {
log.Crit("tm.Init", "err", err)
}
}
}

// If mining is started, we can disable the transaction rejection mechanism
// introduced to speed sync times.
atomic.StoreUint32(&s.handler.acceptTxs, 1)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.15

require (
github.com/Azure/azure-storage-blob-go v0.7.0
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220307211413-49a75ab4c3f2 // indirect
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220307211413-49a75ab4c3f2
github.com/VictoriaMetrics/fastcache v1.6.0
github.com/aws/aws-sdk-go-v2 v1.2.0
github.com/aws/aws-sdk-go-v2/config v1.1.1
Expand Down
7 changes: 2 additions & 5 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
defer timer.Stop()
<-timer.C // discard the initial tick

tm, isTm := w.engine.(*tendermint.Tendermint)
_, isTm := w.engine.(*tendermint.Tendermint)
// commit aborts in-flight transaction execution with given signal and resubmits a new one.
commit := func(noempty bool, s int32) {
if interrupt != nil {
Expand Down Expand Up @@ -457,11 +457,8 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
select {
case <-w.startCh:
clearPending(w.chain.CurrentBlock().NumberU64())

if isTm {
err := tm.Init(w.chain, w.makeBlock)
if err != nil {
log.Crit("tm.Init", "err", err)
}
continue
}

Expand Down
4 changes: 3 additions & 1 deletion params/bootnodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ var Web3QTestnetBootnodes = []string{
}

// TODO: to update
var Web3QGalileoBootnodes = []string{}
var Web3QGalileoBootnodes = []string{
"enode://1b9032f19ae39390e84718071ac8d560f5a11e1b8e02de4a40654a9d2950ae77b34a2ca90854413dfe5973c0040d0928a5cf27a46cd8cc1c6a8fbc1fb7d2825f@127.0.0.1:30303",
}

var Web3QMainnetBootnodes = []string{
"enode://0c2e9b39c4f8655d6cdd3dd3a57bee4fb1c20a3c844507b36bd806d7219048e3df6b7bc3dcc0a1830b59c7d25f983572c11a8ab8469e21a8d937f4ba84d98288@143.244.163.228:30303",
Expand Down
86 changes: 86 additions & 0 deletions params/chamber_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package params

import "time"

// ConsensusConfig defines the configuration for the Tendermint consensus service,
// including timeouts and details about the WAL and the block structure.
type ConsensusConfig struct {
RootDir string `mapstructure:"home"`
WalPath string `mapstructure:"wal-file"`
WalFile string // overrides WalPath if set

// TODO: remove timeout configs, these should be global not local
// How long we wait for a proposal block before prevoting nil
TimeoutPropose time.Duration `mapstructure:"timeout-propose"`
// How much timeout-propose increases with each round
TimeoutProposeDelta time.Duration `mapstructure:"timeout-propose-delta"`
// How long we wait after receiving +2/3 prevotes for “anything” (ie. not a single block or nil)
TimeoutPrevote time.Duration `mapstructure:"timeout-prevote"`
// How much the timeout-prevote increases with each round
TimeoutPrevoteDelta time.Duration `mapstructure:"timeout-prevote-delta"`
// How long we wait after receiving +2/3 precommits for “anything” (ie. not a single block or nil)
TimeoutPrecommit time.Duration `mapstructure:"timeout-precommit"`
// How much the timeout-precommit increases with each round
TimeoutPrecommitDelta time.Duration `mapstructure:"timeout-precommit-delta"`
// How long we wait after committing a block, before starting on the new
// height (this gives us a chance to receive some more precommits, even
// though we already have +2/3).
TimeoutCommit time.Duration `mapstructure:"timeout-commit"`

// Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
SkipTimeoutCommit bool `mapstructure:"skip-timeout-commit"`

// Reactor sleep duration parameters
PeerGossipSleepDuration time.Duration `mapstructure:"peer-gossip-sleep-duration"`
PeerQueryMaj23SleepDuration time.Duration `mapstructure:"peer-query-maj23-sleep-duration"`

ConsensusSyncRequestDuration time.Duration

DoubleSignCheckHeight uint64 `mapstructure:"double-sign-check-height"`
}

func NewDefaultConsesusConfig() *ConsensusConfig {
// Config from tendermint except TimeoutCommit is 5s (original 1s)
return &ConsensusConfig{
// WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
TimeoutPropose: 3000 * time.Millisecond,
TimeoutProposeDelta: 500 * time.Millisecond,
TimeoutPrevote: 1000 * time.Millisecond,
TimeoutPrevoteDelta: 500 * time.Millisecond,
TimeoutPrecommit: 1000 * time.Millisecond,
TimeoutPrecommitDelta: 500 * time.Millisecond,
TimeoutCommit: 5000 * time.Millisecond,
SkipTimeoutCommit: false,
PeerGossipSleepDuration: 100 * time.Millisecond,
PeerQueryMaj23SleepDuration: 2000 * time.Millisecond,
DoubleSignCheckHeight: uint64(0),
ConsensusSyncRequestDuration: 500 * time.Millisecond,
}
}

// Propose returns the amount of time to wait for a proposal
func (cfg *ConsensusConfig) Propose(round int32) time.Duration {
return time.Duration(
cfg.TimeoutPropose.Nanoseconds()+cfg.TimeoutProposeDelta.Nanoseconds()*int64(round),
) * time.Nanosecond
}

// Prevote returns the amount of time to wait for straggler votes after receiving any +2/3 prevotes
func (cfg *ConsensusConfig) Prevote(round int32) time.Duration {
return time.Duration(
cfg.TimeoutPrevote.Nanoseconds()+cfg.TimeoutPrevoteDelta.Nanoseconds()*int64(round),
) * time.Nanosecond
}

// Precommit returns the amount of time to wait for straggler votes after receiving any +2/3 precommits
func (cfg *ConsensusConfig) Precommit(round int32) time.Duration {
return time.Duration(
cfg.TimeoutPrecommit.Nanoseconds()+cfg.TimeoutPrecommitDelta.Nanoseconds()*int64(round),
) * time.Nanosecond
}

// Commit returns the amount of time to wait for straggler votes after receiving +2/3 precommits
// for a single block (ie. a commit).
func (cfg *ConsensusConfig) Commit(t time.Time) time.Time {
return t.Add(cfg.TimeoutCommit)
}
2 changes: 0 additions & 2 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@ var (
ProposerRepetition: 8,
P2pBootstrap: "/ip4/127.0.0.1/udp/33333/quic/p2p/12D3KooWRqZRJf6gYeLgeUnNCnKeRb29KiEVQcvRWk2tet9Q3Hmy",
NodeKeyPath: "/Users/qizhou/.ssh/node_galileo.key",
ValKeyPath: "/Users/qizhou/.ssh/val_galileo.key",
ConsensusConfig: ConsensusConfig{
// WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
TimeoutPropose: 3000 * time.Millisecond,
Expand Down Expand Up @@ -464,7 +463,6 @@ type CliqueConfig struct {

type TendermintConfig struct {
Epoch uint64 `json:"epoch"` // Epoch lengh to vote new validator
ValKeyPath string
NodeKeyPath string
P2pPort uint
NetworkID string
Expand Down

0 comments on commit 5c9a842

Please sign in to comment.