validator contract

This commit is contained in:
Jaynti Kanani 2019-06-09 21:47:25 +05:30
parent 2c630dffda
commit ceae9134b9
No known key found for this signature in database
GPG key ID: 4396982C976BAE5E
11 changed files with 1332 additions and 24 deletions

View file

@ -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) {

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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);
}

View file

@ -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"}]

View file

@ -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
}

View file

@ -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 = "<nil>"
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

View file

@ -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
}

View file

@ -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.