From ceae9134b94dae475e3e25410aa697f0e205ac1c Mon Sep 17 00:00:00 2001 From: Jaynti Kanani Date: Sun, 9 Jun 2019 21:47:25 +0530 Subject: [PATCH] validator contract --- cmd/utils/flags.go | 2 +- consensus/bor/bor.go | 79 +- consensus/bor/validator.go | 88 +++ consensus/bor/validator_set.go | 699 ++++++++++++++++++ .../validatorset/contract/ValidatorSet.sol | 40 + ...contract_ValidatorSet_sol_ValidatorSet.abi | 1 + ...contract_ValidatorSet_sol_ValidatorSet.bin | 0 contracts/validatorset/validatorset.go | 399 ++++++++++ eth/backend.go | 22 +- les/backend.go | 17 +- params/config.go | 9 +- 11 files changed, 1332 insertions(+), 24 deletions(-) create mode 100644 consensus/bor/validator.go create mode 100644 consensus/bor/validator_set.go create mode 100644 contracts/validatorset/contract/ValidatorSet.sol create mode 100644 contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.abi create mode 100644 contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.bin create mode 100644 contracts/validatorset/validatorset.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 3344296177..f44f7c2b91 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1608,7 +1608,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai if config.Clique != nil { engine = clique.New(config.Clique, chainDb) } else if config.Bor != nil { - engine = bor.New(config.Bor, chainDb) + engine = bor.New(config.Bor, chainDb, nil) } else { engine = ethash.NewFaker() if !ctx.GlobalBool(FakePoWFlag.Name) { diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 60383ab4e2..3112571759 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -2,26 +2,34 @@ package bor import ( "bytes" + "context" "errors" + "fmt" "io" + "math" "math/big" "math/rand" + "strings" "sync" "time" "github.com/ethereum/go-ethereum/accounts" + "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/consensus/misc" + "github.com/ethereum/go-ethereum/contracts/validatorset" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + lru "github.com/hashicorp/golang-lru" "golang.org/x/crypto/sha3" ) @@ -215,7 +223,7 @@ func BorRLP(header *types.Header) []byte { // Bor is the matic-bor consensus engine type Bor struct { - config *params.BorConfig // Consensus engine configuration parameters + config *params.BorConfig // Consensus engine configuration parameters for bor consensus db ethdb.Database // Database to store and retrieve snapshot checkpoints recents *lru.ARCCache // Snapshots for recent block to speed up reorgs @@ -227,12 +235,14 @@ type Bor struct { signFn SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields + ethAPI *ethapi.PublicBlockChainAPI + // The fields below are for testing only fakeDiff bool // Skip difficulty verifications } // New creates a Matic Bor consensus engine. -func New(config *params.BorConfig, db ethdb.Database) *Bor { +func New(config *params.BorConfig, db ethdb.Database, ethAPI *ethapi.PublicBlockChainAPI) *Bor { // Set any missing consensus parameters to their defaults conf := *config if conf.Epoch == 0 { @@ -245,6 +255,7 @@ func New(config *params.BorConfig, db ethdb.Database) *Bor { return &Bor{ config: &conf, db: db, + ethAPI: ethAPI, recents: recents, signatures: signatures, proposals: make(map[common.Address]bool), @@ -410,12 +421,17 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H break } } + // If we're at an checkpoint block, make a snapshot if it's known if number == 0 || (number%c.config.Epoch == 0 && chain.GetHeaderByNumber(number-1) == nil) { checkpoint := chain.GetHeaderByNumber(number) if checkpoint != nil { hash := checkpoint.Hash() + // validatorSet := new(ValidatorSet) + // TODO populate validator set + c.GetCurrentValidators(number) + signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength) for i := 0; i < len(signers); i++ { copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:]) @@ -525,6 +541,8 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare } } + log.Info("==> New block", "number", header.Number, "hash", header.Hash().Hex(), "signer", signer.Hex()) + return nil } @@ -652,7 +670,11 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan } // If we're amongst the recent signers, wait for the next block for seen, recent := range snap.Recents { + count := 0 if recent == signer { + // count on how many times signer has been seen + count = count + 1 + // Signer is among recents, only wait if the current block doesn't shift it out if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit { log.Info("Signed recently, must wait for others") @@ -725,3 +747,56 @@ func (c *Bor) APIs(chain consensus.ChainReader) []rpc.API { func (c *Bor) Close() error { return nil } + +// GetCurrentValidators get current validators +func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) { + blockNr := rpc.BlockNumber(number) + + // validator set ABI + validatorSetABI, _ := abi.JSON(strings.NewReader(validatorset.ValidatorsetABI)) + data, err := validatorSetABI.Pack("getValidators") + if err != nil { + fmt.Println("Unable to pack tx for getValidator", "error", err) + return nil, err + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // cancel when we are finished consuming integers + + // call + msgData := (hexutil.Bytes)(data) + toAddress := common.HexToAddress(c.config.ValidatorContract) + gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) + result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ + Gas: &gas, + To: &toAddress, + Data: &msgData, + }, blockNr) + if err != nil { + fmt.Println("err", err) + return nil, err + } + + var ( + ret0 = new([]common.Address) + ret1 = new([]*big.Int) + ) + out := &[]interface{}{ + ret0, + ret1, + } + + if err := validatorSetABI.Unpack(out, "getValidators", result); err != nil { + fmt.Println("err", err) + return nil, err + } + + valz := make([]*Validator, len(*ret0)) + for i, a := range *ret0 { + valz[i] = &Validator{ + Address: a, + VotingPower: (*ret1)[i].Int64(), + } + } + return valz, nil +} diff --git a/consensus/bor/validator.go b/consensus/bor/validator.go new file mode 100644 index 0000000000..c65dd5858c --- /dev/null +++ b/consensus/bor/validator.go @@ -0,0 +1,88 @@ +package bor + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Volatile state for each Validator +// NOTE: The ProposerPriority is not included in Validator.Hash(); +// make sure to update that method if changes are made here +type Validator struct { + Address common.Address `json:"address"` + VotingPower int64 `json:"voting_power"` + ProposerPriority int64 `json:"proposer_priority"` +} + +func NewValidator(address common.Address, votingPower int64) *Validator { + return &Validator{ + Address: address, + VotingPower: votingPower, + ProposerPriority: 0, + } +} + +// Creates a new copy of the validator so we can mutate ProposerPriority. +// Panics if the validator is nil. +func (v *Validator) Copy() *Validator { + vCopy := *v + return &vCopy +} + +// Returns the one with higher ProposerPriority. +func (v *Validator) CompareProposerPriority(other *Validator) *Validator { + if v == nil { + return other + } + if v.ProposerPriority > other.ProposerPriority { + return v + } else if v.ProposerPriority < other.ProposerPriority { + return other + } else { + result := bytes.Compare(v.Address.Bytes(), other.Address.Bytes()) + if result < 0 { + return v + } else if result > 0 { + return other + } else { + panic("Cannot compare identical validators") + return nil + } + } +} + +func (v *Validator) String() string { + if v == nil { + return "nil-Validator" + } + return fmt.Sprintf("Validator{%v VP:%v A:%v}", + v.Address, + v.VotingPower, + v.ProposerPriority) +} + +// ValidatorListString returns a prettified validator list for logging purposes. +func ValidatorListString(vals []*Validator) string { + chunks := make([]string, len(vals)) + for i, val := range vals { + chunks[i] = fmt.Sprintf("%s:%d", val.Address, val.VotingPower) + } + + return strings.Join(chunks, ",") +} + +// Bytes computes the unique encoding of a validator with a given voting power. +// These are the bytes that gets hashed in consensus. It excludes address +// as its redundant with the pubkey. This also excludes ProposerPriority +// which changes every round. +func (v *Validator) Bytes() []byte { + b, err := json.Marshal(v) + if err != nil { + return b + } + return nil +} diff --git a/consensus/bor/validator_set.go b/consensus/bor/validator_set.go new file mode 100644 index 0000000000..1de37a715c --- /dev/null +++ b/consensus/bor/validator_set.go @@ -0,0 +1,699 @@ +package bor + +// Tendermint leader selection algorithm + +import ( + "bytes" + "fmt" + "math" + "math/big" + "sort" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// MaxTotalVotingPower - the maximum allowed total voting power. +// It needs to be sufficiently small to, in all cases: +// 1. prevent clipping in incrementProposerPriority() +// 2. let (diff+diffMax-1) not overflow in IncrementProposerPriority() +// (Proof of 1 is tricky, left to the reader). +// It could be higher, but this is sufficiently large for our purposes, +// and leaves room for defensive purposes. +// PriorityWindowSizeFactor - is a constant that when multiplied with the total voting power gives +// the maximum allowed distance between validator priorities. + +const ( + MaxTotalVotingPower = int64(math.MaxInt64) / 8 + PriorityWindowSizeFactor = 2 +) + +// ValidatorSet represent a set of *Validator at a given height. +// The validators can be fetched by address or index. +// The index is in order of .Address, so the indices are fixed +// for all rounds of a given blockchain height - ie. the validators +// are sorted by their address. +// On the other hand, the .ProposerPriority of each validator and +// the designated .GetProposer() of a set changes every round, +// upon calling .IncrementProposerPriority(). +// NOTE: Not goroutine-safe. +// NOTE: All get/set to validators should copy the value for safety. +type ValidatorSet struct { + // NOTE: persisted via reflect, must be exported. + Validators []*Validator `json:"validators"` + Proposer *Validator `json:"proposer"` + + // cached (unexported) + totalVotingPower int64 +} + +// NewValidatorSet initializes a ValidatorSet by copying over the +// values from `valz`, a list of Validators. If valz is nil or empty, +// the new ValidatorSet will have an empty list of Validators. +// The addresses of validators in `valz` must be unique otherwise the +// function panics. +func NewValidatorSet(valz []*Validator) *ValidatorSet { + vals := &ValidatorSet{} + err := vals.updateWithChangeSet(valz, false) + if err != nil { + panic(fmt.Sprintf("cannot create validator set: %s", err)) + } + if len(valz) > 0 { + vals.IncrementProposerPriority(1) + } + return vals +} + +// Nil or empty validator sets are invalid. +func (vals *ValidatorSet) IsNilOrEmpty() bool { + return vals == nil || len(vals.Validators) == 0 +} + +// Increment ProposerPriority and update the proposer on a copy, and return it. +func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *ValidatorSet { + copy := vals.Copy() + copy.IncrementProposerPriority(times) + return copy +} + +// IncrementProposerPriority increments ProposerPriority of each validator and updates the +// proposer. Panics if validator set is empty. +// `times` must be positive. +func (vals *ValidatorSet) IncrementProposerPriority(times int) { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } + if times <= 0 { + panic("Cannot call IncrementProposerPriority with non-positive times") + } + + // Cap the difference between priorities to be proportional to 2*totalPower by + // re-normalizing priorities, i.e., rescale all priorities by multiplying with: + // 2*totalVotingPower/(maxPriority - minPriority) + diffMax := PriorityWindowSizeFactor * vals.TotalVotingPower() + vals.RescalePriorities(diffMax) + vals.shiftByAvgProposerPriority() + + var proposer *Validator + // Call IncrementProposerPriority(1) times times. + for i := 0; i < times; i++ { + proposer = vals.incrementProposerPriority() + } + + vals.Proposer = proposer +} + +func (vals *ValidatorSet) RescalePriorities(diffMax int64) { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } + // NOTE: This check is merely a sanity check which could be + // removed if all tests would init. voting power appropriately; + // i.e. diffMax should always be > 0 + if diffMax <= 0 { + return + } + + // Calculating ceil(diff/diffMax): + // Re-normalization is performed by dividing by an integer for simplicity. + // NOTE: This may make debugging priority issues easier as well. + diff := computeMaxMinPriorityDiff(vals) + ratio := (diff + diffMax - 1) / diffMax + if diff > diffMax { + for _, val := range vals.Validators { + val.ProposerPriority = val.ProposerPriority / ratio + } + } +} + +func (vals *ValidatorSet) incrementProposerPriority() *Validator { + for _, val := range vals.Validators { + // Check for overflow for sum. + newPrio := safeAddClip(val.ProposerPriority, val.VotingPower) + val.ProposerPriority = newPrio + } + // Decrement the validator with most ProposerPriority. + mostest := vals.getValWithMostPriority() + // Mind the underflow. + mostest.ProposerPriority = safeSubClip(mostest.ProposerPriority, vals.TotalVotingPower()) + + return mostest +} + +// Should not be called on an empty validator set. +func (vals *ValidatorSet) computeAvgProposerPriority() int64 { + n := int64(len(vals.Validators)) + sum := big.NewInt(0) + for _, val := range vals.Validators { + sum.Add(sum, big.NewInt(val.ProposerPriority)) + } + avg := sum.Div(sum, big.NewInt(n)) + if avg.IsInt64() { + return avg.Int64() + } + + // This should never happen: each val.ProposerPriority is in bounds of int64. + panic(fmt.Sprintf("Cannot represent avg ProposerPriority as an int64 %v", avg)) +} + +// Compute the difference between the max and min ProposerPriority of that set. +func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } + max := int64(math.MinInt64) + min := int64(math.MaxInt64) + for _, v := range vals.Validators { + if v.ProposerPriority < min { + min = v.ProposerPriority + } + if v.ProposerPriority > max { + max = v.ProposerPriority + } + } + diff := max - min + if diff < 0 { + return -1 * diff + } else { + return diff + } +} + +func (vals *ValidatorSet) getValWithMostPriority() *Validator { + var res *Validator + for _, val := range vals.Validators { + res = res.CompareProposerPriority(val) + } + return res +} + +func (vals *ValidatorSet) shiftByAvgProposerPriority() { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } + avgProposerPriority := vals.computeAvgProposerPriority() + for _, val := range vals.Validators { + val.ProposerPriority = safeSubClip(val.ProposerPriority, avgProposerPriority) + } +} + +// Makes a copy of the validator list. +func validatorListCopy(valsList []*Validator) []*Validator { + if valsList == nil { + return nil + } + valsCopy := make([]*Validator, len(valsList)) + for i, val := range valsList { + valsCopy[i] = val.Copy() + } + return valsCopy +} + +// Copy each validator into a new ValidatorSet. +func (vals *ValidatorSet) Copy() *ValidatorSet { + return &ValidatorSet{ + Validators: validatorListCopy(vals.Validators), + Proposer: vals.Proposer, + totalVotingPower: vals.totalVotingPower, + } +} + +// HasAddress returns true if address given is in the validator set, false - +// otherwise. +func (vals *ValidatorSet) HasAddress(address []byte) bool { + idx := sort.Search(len(vals.Validators), func(i int) bool { + return bytes.Compare(address, vals.Validators[i].Address.Bytes()) <= 0 + }) + return idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address.Bytes(), address) +} + +// GetByAddress returns an index of the validator with address and validator +// itself if found. Otherwise, -1 and nil are returned. +func (vals *ValidatorSet) GetByAddress(address common.Address) (index int, val *Validator) { + idx := sort.Search(len(vals.Validators), func(i int) bool { + return bytes.Compare(address.Bytes(), vals.Validators[i].Address.Bytes()) <= 0 + }) + if idx < len(vals.Validators) && bytes.Equal(vals.Validators[idx].Address.Bytes(), address.Bytes()) { + return idx, vals.Validators[idx].Copy() + } + return -1, nil +} + +// GetByIndex returns the validator's address and validator itself by index. +// It returns nil values if index is less than 0 or greater or equal to +// len(ValidatorSet.Validators). +func (vals *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) { + if index < 0 || index >= len(vals.Validators) { + return nil, nil + } + val = vals.Validators[index] + return val.Address.Bytes(), val.Copy() +} + +// Size returns the length of the validator set. +func (vals *ValidatorSet) Size() int { + return len(vals.Validators) +} + +// Force recalculation of the set's total voting power. +func (vals *ValidatorSet) updateTotalVotingPower() { + + sum := int64(0) + for _, val := range vals.Validators { + // mind overflow + sum = safeAddClip(sum, val.VotingPower) + if sum > MaxTotalVotingPower { + panic(fmt.Sprintf( + "Total voting power should be guarded to not exceed %v; got: %v", + MaxTotalVotingPower, + sum)) + } + } + + vals.totalVotingPower = sum +} + +// TotalVotingPower returns the sum of the voting powers of all validators. +// It recomputes the total voting power if required. +func (vals *ValidatorSet) TotalVotingPower() int64 { + if vals.totalVotingPower == 0 { + vals.updateTotalVotingPower() + } + return vals.totalVotingPower +} + +// GetProposer returns the current proposer. If the validator set is empty, nil +// is returned. +func (vals *ValidatorSet) GetProposer() (proposer *Validator) { + if len(vals.Validators) == 0 { + return nil + } + if vals.Proposer == nil { + vals.Proposer = vals.findProposer() + } + return vals.Proposer.Copy() +} + +func (vals *ValidatorSet) findProposer() *Validator { + var proposer *Validator + for _, val := range vals.Validators { + if proposer == nil || !bytes.Equal(val.Address.Bytes(), proposer.Address.Bytes()) { + proposer = proposer.CompareProposerPriority(val) + } + } + return proposer +} + +// Hash returns the Merkle root hash build using validators (as leaves) in the +// set. +// func (vals *ValidatorSet) Hash() []byte { +// if len(vals.Validators) == 0 { +// return nil +// } +// bzs := make([][]byte, len(vals.Validators)) +// for i, val := range vals.Validators { +// bzs[i] = val.Bytes() +// } +// return merkle.SimpleHashFromByteSlices(bzs) +// } + +// Iterate will run the given function over the set. +func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { + for i, val := range vals.Validators { + stop := fn(i, val.Copy()) + if stop { + break + } + } +} + +// Checks changes against duplicates, splits the changes in updates and removals, sorts them by address. +// +// Returns: +// updates, removals - the sorted lists of updates and removals +// err - non-nil if duplicate entries or entries with negative voting power are seen +// +// No changes are made to 'origChanges'. +func processChanges(origChanges []*Validator) (updates, removals []*Validator, err error) { + // Make a deep copy of the changes and sort by address. + changes := validatorListCopy(origChanges) + sort.Sort(ValidatorsByAddress(changes)) + + removals = make([]*Validator, 0, len(changes)) + updates = make([]*Validator, 0, len(changes)) + var prevAddr common.Address + + // Scan changes by address and append valid validators to updates or removals lists. + for _, valUpdate := range changes { + if bytes.Equal(valUpdate.Address.Bytes(), prevAddr.Bytes()) { + err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes) + return nil, nil, err + } + if valUpdate.VotingPower < 0 { + err = fmt.Errorf("voting power can't be negative: %v", valUpdate) + return nil, nil, err + } + if valUpdate.VotingPower > MaxTotalVotingPower { + err = fmt.Errorf("to prevent clipping/ overflow, voting power can't be higher than %v: %v ", + MaxTotalVotingPower, valUpdate) + return nil, nil, err + } + if valUpdate.VotingPower == 0 { + removals = append(removals, valUpdate) + } else { + updates = append(updates, valUpdate) + } + prevAddr = valUpdate.Address + } + return updates, removals, err +} + +// Verifies a list of updates against a validator set, making sure the allowed +// total voting power would not be exceeded if these updates would be applied to the set. +// +// Returns: +// updatedTotalVotingPower - the new total voting power if these updates would be applied +// numNewValidators - number of new validators +// err - non-nil if the maximum allowed total voting power would be exceeded +// +// 'updates' should be a list of proper validator changes, i.e. they have been verified +// by processChanges for duplicates and invalid values. +// No changes are made to the validator set 'vals'. +func verifyUpdates(updates []*Validator, vals *ValidatorSet) (updatedTotalVotingPower int64, numNewValidators int, err error) { + + updatedTotalVotingPower = vals.TotalVotingPower() + + for _, valUpdate := range updates { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + // New validator, add its voting power the the total. + updatedTotalVotingPower += valUpdate.VotingPower + numNewValidators++ + } else { + // Updated validator, add the difference in power to the total. + updatedTotalVotingPower += valUpdate.VotingPower - val.VotingPower + } + overflow := updatedTotalVotingPower > MaxTotalVotingPower + if overflow { + err = fmt.Errorf( + "failed to add/update validator %v, total voting power would exceed the max allowed %v", + valUpdate, MaxTotalVotingPower) + return 0, 0, err + } + } + + return updatedTotalVotingPower, numNewValidators, nil +} + +// Computes the proposer priority for the validators not present in the set based on 'updatedTotalVotingPower'. +// Leaves unchanged the priorities of validators that are changed. +// +// 'updates' parameter must be a list of unique validators to be added or updated. +// No changes are made to the validator set 'vals'. +func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotalVotingPower int64) { + + for _, valUpdate := range updates { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + // add val + // Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't + // un-bond and then re-bond to reset their (potentially previously negative) ProposerPriority to zero. + // + // Contract: updatedVotingPower < MaxTotalVotingPower to ensure ProposerPriority does + // not exceed the bounds of int64. + // + // Compute ProposerPriority = -1.125*totalVotingPower == -(updatedVotingPower + (updatedVotingPower >> 3)). + valUpdate.ProposerPriority = -(updatedTotalVotingPower + (updatedTotalVotingPower >> 3)) + } else { + valUpdate.ProposerPriority = val.ProposerPriority + } + } + +} + +// Merges the vals' validator list with the updates list. +// When two elements with same address are seen, the one from updates is selected. +// Expects updates to be a list of updates sorted by address with no duplicates or errors, +// must have been validated with verifyUpdates() and priorities computed with computeNewPriorities(). +func (vals *ValidatorSet) applyUpdates(updates []*Validator) { + + existing := vals.Validators + merged := make([]*Validator, len(existing)+len(updates)) + i := 0 + + for len(existing) > 0 && len(updates) > 0 { + if bytes.Compare(existing[0].Address.Bytes(), updates[0].Address.Bytes()) < 0 { // unchanged validator + merged[i] = existing[0] + existing = existing[1:] + } else { + // Apply add or update. + merged[i] = updates[0] + if bytes.Equal(existing[0].Address.Bytes(), updates[0].Address.Bytes()) { + // Validator is present in both, advance existing. + existing = existing[1:] + } + updates = updates[1:] + } + i++ + } + + // Add the elements which are left. + for j := 0; j < len(existing); j++ { + merged[i] = existing[j] + i++ + } + // OR add updates which are left. + for j := 0; j < len(updates); j++ { + merged[i] = updates[j] + i++ + } + + vals.Validators = merged[:i] +} + +// Checks that the validators to be removed are part of the validator set. +// No changes are made to the validator set 'vals'. +func verifyRemovals(deletes []*Validator, vals *ValidatorSet) error { + + for _, valUpdate := range deletes { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + return fmt.Errorf("failed to find validator %X to remove", address) + } + } + if len(deletes) > len(vals.Validators) { + panic("more deletes than validators") + } + return nil +} + +// Removes the validators specified in 'deletes' from validator set 'vals'. +// Should not fail as verification has been done before. +func (vals *ValidatorSet) applyRemovals(deletes []*Validator) { + + existing := vals.Validators + + merged := make([]*Validator, len(existing)-len(deletes)) + i := 0 + + // Loop over deletes until we removed all of them. + for len(deletes) > 0 { + if bytes.Equal(existing[0].Address.Bytes(), deletes[0].Address.Bytes()) { + deletes = deletes[1:] + } else { // Leave it in the resulting slice. + merged[i] = existing[0] + i++ + } + existing = existing[1:] + } + + // Add the elements which are left. + for j := 0; j < len(existing); j++ { + merged[i] = existing[j] + i++ + } + + vals.Validators = merged[:i] +} + +// Main function used by UpdateWithChangeSet() and NewValidatorSet(). +// If 'allowDeletes' is false then delete operations (identified by validators with voting power 0) +// are not allowed and will trigger an error if present in 'changes'. +// The 'allowDeletes' flag is set to false by NewValidatorSet() and to true by UpdateWithChangeSet(). +func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes bool) error { + + if len(changes) <= 0 { + return nil + } + + // Check for duplicates within changes, split in 'updates' and 'deletes' lists (sorted). + updates, deletes, err := processChanges(changes) + if err != nil { + return err + } + + if !allowDeletes && len(deletes) != 0 { + return fmt.Errorf("cannot process validators with voting power 0: %v", deletes) + } + + // Verify that applying the 'deletes' against 'vals' will not result in error. + if err := verifyRemovals(deletes, vals); err != nil { + return err + } + + // Verify that applying the 'updates' against 'vals' will not result in error. + updatedTotalVotingPower, numNewValidators, err := verifyUpdates(updates, vals) + if err != nil { + return err + } + + // Check that the resulting set will not be empty. + if numNewValidators == 0 && len(vals.Validators) == len(deletes) { + return fmt.Errorf("applying the validator changes would result in empty set") + } + + // Compute the priorities for updates. + computeNewPriorities(updates, vals, updatedTotalVotingPower) + + // Apply updates and removals. + vals.applyUpdates(updates) + vals.applyRemovals(deletes) + + vals.updateTotalVotingPower() + + // Scale and center. + vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower()) + vals.shiftByAvgProposerPriority() + + return nil +} + +// UpdateWithChangeSet attempts to update the validator set with 'changes'. +// It performs the following steps: +// - validates the changes making sure there are no duplicates and splits them in updates and deletes +// - verifies that applying the changes will not result in errors +// - computes the total voting power BEFORE removals to ensure that in the next steps the priorities +// across old and newly added validators are fair +// - computes the priorities of new validators against the final set +// - applies the updates against the validator set +// - applies the removals against the validator set +// - performs scaling and centering of priority values +// If an error is detected during verification steps, it is returned and the validator set +// is not changed. +func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { + return vals.updateWithChangeSet(changes, true) +} + +//----------------- +// ErrTooMuchChange + +func IsErrTooMuchChange(err error) bool { + switch err.(type) { + case errTooMuchChange: + return true + default: + return false + } +} + +type errTooMuchChange struct { + got int64 + needed int64 +} + +func (e errTooMuchChange) Error() string { + return fmt.Sprintf("Invalid commit -- insufficient old voting power: got %v, needed %v", e.got, e.needed) +} + +//---------------- + +func (vals *ValidatorSet) String() string { + return vals.StringIndented("") +} + +func (vals *ValidatorSet) StringIndented(indent string) string { + if vals == nil { + return "nil-ValidatorSet" + } + var valStrings []string + vals.Iterate(func(index int, val *Validator) bool { + valStrings = append(valStrings, val.String()) + return false + }) + return fmt.Sprintf(`ValidatorSet{ +%s Proposer: %v +%s Validators: +%s %v +%s}`, + indent, vals.GetProposer().String(), + indent, + indent, strings.Join(valStrings, "\n"+indent+" "), + indent) + +} + +//------------------------------------- +// Implements sort for sorting validators by address. + +// Sort validators by address. +type ValidatorsByAddress []*Validator + +func (valz ValidatorsByAddress) Len() int { + return len(valz) +} + +func (valz ValidatorsByAddress) Less(i, j int) bool { + return bytes.Compare(valz[i].Address.Bytes(), valz[j].Address.Bytes()) == -1 +} + +func (valz ValidatorsByAddress) Swap(i, j int) { + it := valz[i] + valz[i] = valz[j] + valz[j] = it +} + +/////////////////////////////////////////////////////////////////////////////// +// safe addition/subtraction + +func safeAdd(a, b int64) (int64, bool) { + if b > 0 && a > math.MaxInt64-b { + return -1, true + } else if b < 0 && a < math.MinInt64-b { + return -1, true + } + return a + b, false +} + +func safeSub(a, b int64) (int64, bool) { + if b > 0 && a < math.MinInt64+b { + return -1, true + } else if b < 0 && a > math.MaxInt64+b { + return -1, true + } + return a - b, false +} + +func safeAddClip(a, b int64) int64 { + c, overflow := safeAdd(a, b) + if overflow { + if b < 0 { + return math.MinInt64 + } + return math.MaxInt64 + } + return c +} + +func safeSubClip(a, b int64) int64 { + c, overflow := safeSub(a, b) + if overflow { + if b > 0 { + return math.MinInt64 + } + return math.MaxInt64 + } + return c +} diff --git a/contracts/validatorset/contract/ValidatorSet.sol b/contracts/validatorset/contract/ValidatorSet.sol new file mode 100644 index 0000000000..29fec4a45c --- /dev/null +++ b/contracts/validatorset/contract/ValidatorSet.sol @@ -0,0 +1,40 @@ +pragma solidity 0.5.9; + +interface ValidatorSet { + /// Issue this log event to signal a desired change in validator set. + /// This will not lead to a change in active validator set until + /// finalizeChange is called. + /// + /// Only the last log event of any block can take effect. + /// If a signal is issued while another is being finalized it may never + /// take effect. + /// + /// _parentHash here should be the parent block hash, or the + /// signal will not be recognized. + event InitiateChange(bytes32 indexed _parentHash, address[] _newSet); + + /// Called when an initiated change reaches finality and is activated. + /// Only valid when msg.sender == SYSTEM (EIP96, 2**160 - 2). + /// + /// Also called when the contract is first enabled for consensus. In this case, + /// the "change" finalized is the activation of the initial set. + function finalizeChange() + external; + + /// Reports benign misbehavior of validator of the current validator set + /// (e.g. validator offline). + function reportBenign(address validator, uint256 blockNumber) + external; + + /// Reports malicious misbehavior of validator of the current validator set + /// and provides proof of that misbehavor, which varies by engine + /// (e.g. double vote). + function reportMalicious(address validator, uint256 blockNumber, bytes calldata proof) + external; + + /// Get current validator set (last enacted or initial if no changes ever made) with current stake. + function getValidators() + external + view + returns (address[] memory, uint256[] memory); +} \ No newline at end of file diff --git a/contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.abi b/contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.abi new file mode 100644 index 0000000000..367a36292d --- /dev/null +++ b/contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"},{"name":"proof","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_parentHash","type":"bytes32"},{"indexed":false,"name":"_newSet","type":"address[]"}],"name":"InitiateChange","type":"event"}] \ No newline at end of file diff --git a/contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.bin b/contracts/validatorset/contract/contract_ValidatorSet_sol_ValidatorSet.bin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contracts/validatorset/validatorset.go b/contracts/validatorset/validatorset.go new file mode 100644 index 0000000000..c64bf8614c --- /dev/null +++ b/contracts/validatorset/validatorset.go @@ -0,0 +1,399 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package validatorset + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = abi.U256 + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// ValidatorsetABI is the input ABI used to generate the binding from. +const ValidatorsetABI = "[{\"constant\":false,\"inputs\":[],\"name\":\"finalizeChange\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"},{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"validator\",\"type\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"name\":\"proof\",\"type\":\"bytes\"}],\"name\":\"reportMalicious\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"validator\",\"type\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"reportBenign\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"_newSet\",\"type\":\"address[]\"}],\"name\":\"InitiateChange\",\"type\":\"event\"}]" + +// Validatorset is an auto generated Go binding around an Ethereum contract. +type Validatorset struct { + ValidatorsetCaller // Read-only binding to the contract + ValidatorsetTransactor // Write-only binding to the contract + ValidatorsetFilterer // Log filterer for contract events +} + +// ValidatorsetCaller is an auto generated read-only Go binding around an Ethereum contract. +type ValidatorsetCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ValidatorsetTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ValidatorsetTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ValidatorsetFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ValidatorsetFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ValidatorsetSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ValidatorsetSession struct { + Contract *Validatorset // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ValidatorsetCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ValidatorsetCallerSession struct { + Contract *ValidatorsetCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ValidatorsetTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ValidatorsetTransactorSession struct { + Contract *ValidatorsetTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ValidatorsetRaw is an auto generated low-level Go binding around an Ethereum contract. +type ValidatorsetRaw struct { + Contract *Validatorset // Generic contract binding to access the raw methods on +} + +// ValidatorsetCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ValidatorsetCallerRaw struct { + Contract *ValidatorsetCaller // Generic read-only contract binding to access the raw methods on +} + +// ValidatorsetTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ValidatorsetTransactorRaw struct { + Contract *ValidatorsetTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewValidatorset creates a new instance of Validatorset, bound to a specific deployed contract. +func NewValidatorset(address common.Address, backend bind.ContractBackend) (*Validatorset, error) { + contract, err := bindValidatorset(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Validatorset{ValidatorsetCaller: ValidatorsetCaller{contract: contract}, ValidatorsetTransactor: ValidatorsetTransactor{contract: contract}, ValidatorsetFilterer: ValidatorsetFilterer{contract: contract}}, nil +} + +// NewValidatorsetCaller creates a new read-only instance of Validatorset, bound to a specific deployed contract. +func NewValidatorsetCaller(address common.Address, caller bind.ContractCaller) (*ValidatorsetCaller, error) { + contract, err := bindValidatorset(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ValidatorsetCaller{contract: contract}, nil +} + +// NewValidatorsetTransactor creates a new write-only instance of Validatorset, bound to a specific deployed contract. +func NewValidatorsetTransactor(address common.Address, transactor bind.ContractTransactor) (*ValidatorsetTransactor, error) { + contract, err := bindValidatorset(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ValidatorsetTransactor{contract: contract}, nil +} + +// NewValidatorsetFilterer creates a new log filterer instance of Validatorset, bound to a specific deployed contract. +func NewValidatorsetFilterer(address common.Address, filterer bind.ContractFilterer) (*ValidatorsetFilterer, error) { + contract, err := bindValidatorset(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ValidatorsetFilterer{contract: contract}, nil +} + +// bindValidatorset binds a generic wrapper to an already deployed contract. +func bindValidatorset(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ValidatorsetABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Validatorset *ValidatorsetRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Validatorset.Contract.ValidatorsetCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Validatorset *ValidatorsetRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Validatorset.Contract.ValidatorsetTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Validatorset *ValidatorsetRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Validatorset.Contract.ValidatorsetTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Validatorset *ValidatorsetCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Validatorset.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Validatorset *ValidatorsetTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Validatorset.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Validatorset *ValidatorsetTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Validatorset.Contract.contract.Transact(opts, method, params...) +} + +// GetValidators is a free data retrieval call binding the contract method 0xb7ab4db5. +// +// Solidity: function getValidators() constant returns(address[], uint256[]) +func (_Validatorset *ValidatorsetCaller) GetValidators(opts *bind.CallOpts) ([]common.Address, []*big.Int, error) { + var ( + ret0 = new([]common.Address) + ret1 = new([]*big.Int) + ) + out := &[]interface{}{ + ret0, + ret1, + } + err := _Validatorset.contract.Call(opts, out, "getValidators") + return *ret0, *ret1, err +} + +// GetValidators is a free data retrieval call binding the contract method 0xb7ab4db5. +// +// Solidity: function getValidators() constant returns(address[], uint256[]) +func (_Validatorset *ValidatorsetSession) GetValidators() ([]common.Address, []*big.Int, error) { + return _Validatorset.Contract.GetValidators(&_Validatorset.CallOpts) +} + +// GetValidators is a free data retrieval call binding the contract method 0xb7ab4db5. +// +// Solidity: function getValidators() constant returns(address[], uint256[]) +func (_Validatorset *ValidatorsetCallerSession) GetValidators() ([]common.Address, []*big.Int, error) { + return _Validatorset.Contract.GetValidators(&_Validatorset.CallOpts) +} + +// FinalizeChange is a paid mutator transaction binding the contract method 0x75286211. +// +// Solidity: function finalizeChange() returns() +func (_Validatorset *ValidatorsetTransactor) FinalizeChange(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Validatorset.contract.Transact(opts, "finalizeChange") +} + +// FinalizeChange is a paid mutator transaction binding the contract method 0x75286211. +// +// Solidity: function finalizeChange() returns() +func (_Validatorset *ValidatorsetSession) FinalizeChange() (*types.Transaction, error) { + return _Validatorset.Contract.FinalizeChange(&_Validatorset.TransactOpts) +} + +// FinalizeChange is a paid mutator transaction binding the contract method 0x75286211. +// +// Solidity: function finalizeChange() returns() +func (_Validatorset *ValidatorsetTransactorSession) FinalizeChange() (*types.Transaction, error) { + return _Validatorset.Contract.FinalizeChange(&_Validatorset.TransactOpts) +} + +// ReportBenign is a paid mutator transaction binding the contract method 0xd69f13bb. +// +// Solidity: function reportBenign(address validator, uint256 blockNumber) returns() +func (_Validatorset *ValidatorsetTransactor) ReportBenign(opts *bind.TransactOpts, validator common.Address, blockNumber *big.Int) (*types.Transaction, error) { + return _Validatorset.contract.Transact(opts, "reportBenign", validator, blockNumber) +} + +// ReportBenign is a paid mutator transaction binding the contract method 0xd69f13bb. +// +// Solidity: function reportBenign(address validator, uint256 blockNumber) returns() +func (_Validatorset *ValidatorsetSession) ReportBenign(validator common.Address, blockNumber *big.Int) (*types.Transaction, error) { + return _Validatorset.Contract.ReportBenign(&_Validatorset.TransactOpts, validator, blockNumber) +} + +// ReportBenign is a paid mutator transaction binding the contract method 0xd69f13bb. +// +// Solidity: function reportBenign(address validator, uint256 blockNumber) returns() +func (_Validatorset *ValidatorsetTransactorSession) ReportBenign(validator common.Address, blockNumber *big.Int) (*types.Transaction, error) { + return _Validatorset.Contract.ReportBenign(&_Validatorset.TransactOpts, validator, blockNumber) +} + +// ReportMalicious is a paid mutator transaction binding the contract method 0xc476dd40. +// +// Solidity: function reportMalicious(address validator, uint256 blockNumber, bytes proof) returns() +func (_Validatorset *ValidatorsetTransactor) ReportMalicious(opts *bind.TransactOpts, validator common.Address, blockNumber *big.Int, proof []byte) (*types.Transaction, error) { + return _Validatorset.contract.Transact(opts, "reportMalicious", validator, blockNumber, proof) +} + +// ReportMalicious is a paid mutator transaction binding the contract method 0xc476dd40. +// +// Solidity: function reportMalicious(address validator, uint256 blockNumber, bytes proof) returns() +func (_Validatorset *ValidatorsetSession) ReportMalicious(validator common.Address, blockNumber *big.Int, proof []byte) (*types.Transaction, error) { + return _Validatorset.Contract.ReportMalicious(&_Validatorset.TransactOpts, validator, blockNumber, proof) +} + +// ReportMalicious is a paid mutator transaction binding the contract method 0xc476dd40. +// +// Solidity: function reportMalicious(address validator, uint256 blockNumber, bytes proof) returns() +func (_Validatorset *ValidatorsetTransactorSession) ReportMalicious(validator common.Address, blockNumber *big.Int, proof []byte) (*types.Transaction, error) { + return _Validatorset.Contract.ReportMalicious(&_Validatorset.TransactOpts, validator, blockNumber, proof) +} + +// ValidatorsetInitiateChangeIterator is returned from FilterInitiateChange and is used to iterate over the raw logs and unpacked data for InitiateChange events raised by the Validatorset contract. +type ValidatorsetInitiateChangeIterator struct { + Event *ValidatorsetInitiateChange // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ValidatorsetInitiateChangeIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ValidatorsetInitiateChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ValidatorsetInitiateChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ValidatorsetInitiateChangeIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ValidatorsetInitiateChangeIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ValidatorsetInitiateChange represents a InitiateChange event raised by the Validatorset contract. +type ValidatorsetInitiateChange struct { + ParentHash [32]byte + NewSet []common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitiateChange is a free log retrieval operation binding the contract event 0x55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89. +// +// Solidity: event InitiateChange(bytes32 indexed _parentHash, address[] _newSet) +func (_Validatorset *ValidatorsetFilterer) FilterInitiateChange(opts *bind.FilterOpts, _parentHash [][32]byte) (*ValidatorsetInitiateChangeIterator, error) { + + var _parentHashRule []interface{} + for _, _parentHashItem := range _parentHash { + _parentHashRule = append(_parentHashRule, _parentHashItem) + } + + logs, sub, err := _Validatorset.contract.FilterLogs(opts, "InitiateChange", _parentHashRule) + if err != nil { + return nil, err + } + return &ValidatorsetInitiateChangeIterator{contract: _Validatorset.contract, event: "InitiateChange", logs: logs, sub: sub}, nil +} + +// WatchInitiateChange is a free log subscription operation binding the contract event 0x55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89. +// +// Solidity: event InitiateChange(bytes32 indexed _parentHash, address[] _newSet) +func (_Validatorset *ValidatorsetFilterer) WatchInitiateChange(opts *bind.WatchOpts, sink chan<- *ValidatorsetInitiateChange, _parentHash [][32]byte) (event.Subscription, error) { + + var _parentHashRule []interface{} + for _, _parentHashItem := range _parentHash { + _parentHashRule = append(_parentHashRule, _parentHashItem) + } + + logs, sub, err := _Validatorset.contract.WatchLogs(opts, "InitiateChange", _parentHashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ValidatorsetInitiateChange) + if err := _Validatorset.contract.UnpackLog(event, "InitiateChange", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} diff --git a/eth/backend.go b/eth/backend.go index 56a1c5dedd..adc3d06cf1 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -136,7 +136,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { chainDb: chainDb, eventMux: ctx.EventMux, accountManager: ctx.AccountManager, - engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb), + engine: nil, shutdownChan: make(chan bool), networkID: config.NetworkId, gasPrice: config.Miner.GasPrice, @@ -145,6 +145,15 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), } + eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil} + gpoParams := config.GPO + if gpoParams.Default == nil { + gpoParams.Default = config.Miner.GasPrice + } + eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) + ethAPI := ethapi.NewPublicBlockChainAPI(eth.APIBackend) + eth.engine = CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb, ethAPI) + bcVersion := rawdb.ReadDatabaseVersion(chainDb) var dbVer = "" if bcVersion != nil { @@ -199,13 +208,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) - eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.Miner.GasPrice - } - eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) - return eth, nil } @@ -227,7 +229,7 @@ func makeExtraData(extra []byte) []byte { } // CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service -func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, ee *ethapi.PublicBlockChainAPI) consensus.Engine { // If proof-of-authority is requested, set it up if chainConfig.Clique != nil { return clique.New(chainConfig.Clique, db) @@ -235,7 +237,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo // If Matic Bor is requested, set it up if chainConfig.Bor != nil { - return bor.New(chainConfig.Bor, db) + return bor.New(chainConfig.Bor, db, ee) } // Otherwise assume proof-of-work diff --git a/les/backend.go b/les/backend.go index 887f882108..5081504d1f 100644 --- a/les/backend.go +++ b/les/backend.go @@ -103,13 +103,22 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { peers: peers, reqDist: newRequestDistributor(peers, quitSync, &mclock.System{}), accountManager: ctx.AccountManager, - engine: eth.CreateConsensusEngine(ctx, chainConfig, &config.Ethash, nil, false, chainDb), + engine: nil, shutdownChan: make(chan bool), networkId: config.NetworkId, bloomRequests: make(chan chan *bloombits.Retrieval), bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), } + leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil} + + gpoParams := config.GPO + if gpoParams.Default == nil { + gpoParams.Default = config.Miner.GasPrice + } + leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) + leth.engine = eth.CreateConsensusEngine(ctx, chainConfig, &config.Ethash, nil, false, chainDb, ethapi.NewPublicBlockChainAPI(leth.ApiBackend)) + var trustedNodes []string if leth.config.ULC != nil { trustedNodes = leth.config.ULC.TrustedServers @@ -166,13 +175,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction) leth.blockchain.DisableCheckFreq() } - leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.Miner.GasPrice - } - leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) return leth, nil } diff --git a/params/config.go b/params/config.go index 77870cd1ee..6b4c1e7987 100644 --- a/params/config.go +++ b/params/config.go @@ -228,10 +228,11 @@ func (c *CliqueConfig) String() string { // BorConfig is the consensus engine configs for Matic bor based sealing. type BorConfig struct { - Period uint64 `json:"period"` // Number of seconds between blocks to enforce - ProducerInterval uint64 `json:"producerInterval"` // Number of seconds between change in block producer interval to enforce - ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval - Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint + Period uint64 `json:"period"` // Number of seconds between blocks to enforce + ProducerInterval uint64 `json:"producerInterval"` // Number of seconds between change in block producer interval to enforce + ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval + Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint + ValidatorContract string `json:"validatorContract"` // Validator set contract } // String implements the stringer interface, returning the consensus engine details.