fix block time bug

This commit is contained in:
Jaynti Kanani 2019-06-21 17:40:23 +05:30
parent ad198aeefd
commit f12ae309cf
No known key found for this signature in database
GPG key ID: 4396982C976BAE5E
4 changed files with 381 additions and 254 deletions

View file

@ -8,7 +8,6 @@ import (
"io" "io"
"math" "math"
"math/big" "math/big"
"math/rand"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -39,7 +38,7 @@ const (
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers wiggleTime = 1000 * time.Millisecond // Random delay (per signer) to allow concurrent signers
) )
// Bor protocol constants. // Bor protocol constants.
@ -116,9 +115,9 @@ var (
// the previous block's timestamp + the minimum block period. // the previous block's timestamp + the minimum block period.
ErrInvalidTimestamp = errors.New("invalid timestamp") ErrInvalidTimestamp = errors.New("invalid timestamp")
// errInvalidVotingChain is returned if an authorization list is attempted to // errOutOfRangeChain is returned if an authorization list is attempted to
// be modified via out-of-range or non-contiguous headers. // be modified via out-of-range or non-contiguous headers.
errInvalidVotingChain = errors.New("invalid voting chain") errOutOfRangeChain = errors.New("out of range or non-contiguous chain")
// errUnauthorizedSigner is returned if a header is signed by a non-authorized entity. // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
errUnauthorizedSigner = errors.New("unauthorized signer") errUnauthorizedSigner = errors.New("unauthorized signer")
@ -191,21 +190,26 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have based on the previous blocks in the chain and the // that a new block should have based on the previous blocks in the chain and the
// current signer. // current signer.
func CalcDifficulty(snap *Snapshot, signer common.Address, producerPeriod uint64) *big.Int { func CalcDifficulty(snap *Snapshot, signer common.Address, epoch uint64) *big.Int {
if snap.inturn(snap.Number+1, signer, producerPeriod) { return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, epoch))
return new(big.Int).Set(diffInTurn)
}
return new(big.Int).Set(diffNoTurn)
} }
// CalcProducerDelay is the producer delay algorithm based on block time. // CalcProducerDelay is the producer delay algorithm based on block time.
func CalcProducerDelay(snap *Snapshot, producerInterval uint64, producerDelay uint64) uint64 { func CalcProducerDelay(snap *Snapshot, signer common.Address, period uint64, epoch uint64, producerDelay uint64) uint64 {
isFirstBlock := (snap.Number + 1) % producerInterval // lastSigner := snap.Recents[snap.Number]
if isFirstBlock == 0 { // proposer := snap.ValidatorSet.GetProposer()
// if block is epoch start block, proposer will be inturn signer
if (snap.Number+1)%epoch == 0 {
fmt.Println("==>", "producerDelay", producerDelay, "(snap.Number+1)%epoch", (snap.Number+1)%epoch)
return producerDelay return producerDelay
// if block is not epoch block, last block signer will be inturn
// } else if bytes.Compare(lastSigner.Bytes(), signer.Bytes()) != 0 {
// return producerDelay
} }
return 0 return period
} }
// BorRLP returns the rlp bytes which needs to be signed for the bor // BorRLP returns the rlp bytes which needs to be signed for the bor
@ -235,7 +239,8 @@ type Bor struct {
signFn SignerFn // Signer function to authorize hashes with signFn SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields lock sync.RWMutex // Protects the signer fields
ethAPI *ethapi.PublicBlockChainAPI ethAPI *ethapi.PublicBlockChainAPI
validatorSetABI abi.ABI
// The fields below are for testing only // The fields below are for testing only
fakeDiff bool // Skip difficulty verifications fakeDiff bool // Skip difficulty verifications
@ -248,18 +253,21 @@ func New(config *params.BorConfig, db ethdb.Database, ethAPI *ethapi.PublicBlock
if conf.Epoch == 0 { if conf.Epoch == 0 {
conf.Epoch = epochLength conf.Epoch = epochLength
} }
// Allocate the snapshot caches and create the engine // Allocate the snapshot caches and create the engine
recents, _ := lru.NewARC(inmemorySnapshots) recents, _ := lru.NewARC(inmemorySnapshots)
signatures, _ := lru.NewARC(inmemorySignatures) signatures, _ := lru.NewARC(inmemorySignatures)
validatorSetABI, _ := abi.JSON(strings.NewReader(validatorset.ValidatorsetABI))
return &Bor{ c := &Bor{
config: &conf, config: &conf,
db: db, db: db,
ethAPI: ethAPI, ethAPI: ethAPI,
recents: recents, recents: recents,
signatures: signatures, signatures: signatures,
proposals: make(map[common.Address]bool), proposals: make(map[common.Address]bool),
validatorSetABI: validatorSetABI,
} }
return c
} }
// Author implements consensus.Engine, returning the Ethereum address recovered // Author implements consensus.Engine, returning the Ethereum address recovered
@ -381,20 +389,22 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H
return ErrInvalidTimestamp return ErrInvalidTimestamp
} }
// Retrieve the snapshot needed to verify this header and cache it // Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) // snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
if err != nil { // if err != nil {
return err // return err
} // }
// If the block is a checkpoint block, verify the signer list // If the block is a checkpoint block, verify the signer list
// TODO verify signers
if number%c.config.Epoch == 0 { if number%c.config.Epoch == 0 {
signers := make([]byte, len(snap.Signers)*common.AddressLength) // signers := make([]byte, len(snap.Signers)*common.AddressLength)
for i, signer := range snap.signers() { // for i, signer := range snap.signers() {
copy(signers[i*common.AddressLength:], signer[:]) // copy(signers[i*common.AddressLength:], signer[:])
} // }
extraSuffix := len(header.Extra) - extraSeal // extraSuffix := len(header.Extra) - extraSeal
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) { // if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
return errMismatchingCheckpointSigners // return errMismatchingCheckpointSigners
} // }
} }
// All basic checks passed, verify the seal and return // All basic checks passed, verify the seal and return
return c.verifySeal(chain, header, parents) return c.verifySeal(chain, header, parents)
@ -415,7 +425,7 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H
} }
// If an on-disk checkpoint snapshot can be found, use that // If an on-disk checkpoint snapshot can be found, use that
if number%voteSnapshotInterval == 0 { if number%voteSnapshotInterval == 0 {
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil { if s, err := loadSnapshot(c.config, c.signatures, c.db, hash, c.ethAPI); err == nil {
log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash) log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash)
snap = s snap = s
break break
@ -428,15 +438,19 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H
if checkpoint != nil { if checkpoint != nil {
hash := checkpoint.Hash() hash := checkpoint.Hash()
// validatorSet := new(ValidatorSet) // current validators
// TODO populate validator set validators, err := c.GetCurrentValidators(number)
c.GetCurrentValidators(number) if err != nil {
// Handle error
}
// TODO remove signer
signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength) signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength)
for i := 0; i < len(signers); i++ { for i := 0; i < len(signers); i++ {
copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:]) copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:])
} }
snap = newSnapshot(c.config, c.signatures, number, hash, signers)
snap = newSnapshot(c.config, c.signatures, number, hash, validators, c.ethAPI)
if err := snap.store(c.db); err != nil { if err := snap.store(c.db); err != nil {
return nil, err return nil, err
} }
@ -519,30 +533,43 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare
if err != nil { if err != nil {
return err return err
} }
if _, ok := snap.Signers[signer]; !ok {
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
return errUnauthorizedSigner return errUnauthorizedSigner
} }
for seen, recent := range snap.Recents {
if recent == signer { // check if signer is correct
// Signer is among recents, only fail if the current block doesn't shift it out validators := snap.ValidatorSet.Validators
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { // proposer will be the last signer if block is not epoch block
// return errRecentlySigned proposer := snap.ValidatorSet.GetProposer().Address
} if number%c.config.Epoch != 0 {
// proposer = snap.Recents[number-1]
}
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
limit := len(validators) - (len(validators)/2 + 1)
// temp index
tempIndex := signerIndex
if proposerIndex != tempIndex && limit > 0 {
if tempIndex < proposerIndex {
tempIndex = tempIndex + len(validators)
}
if tempIndex-proposerIndex > limit {
return errRecentlySigned
} }
} }
// Ensure that the difficulty corresponds to the turn-ness of the signer // Ensure that the difficulty corresponds to the turn-ness of the signer
if !c.fakeDiff { if !c.fakeDiff {
inturn := snap.inturn(header.Number.Uint64(), signer, c.config.ProducerInterval) difficulty := snap.inturn(header.Number.Uint64(), signer, c.config.Epoch)
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 { if header.Difficulty.Uint64() != difficulty {
return errWrongDifficulty
}
if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
return errWrongDifficulty return errWrongDifficulty
} }
} }
log.Info("==> New block", "number", header.Number, "hash", header.Hash().Hex(), "signer", signer.Hex()) fmt.Println("==> New block", "number", header.Number, "hash", header.Hash().Hex(), "signer", signer.Hex(), "proposer", proposer.Hex())
return nil return nil
} }
@ -559,29 +586,9 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
if err != nil { if err != nil {
return err return err
} }
if number%c.config.Epoch != 0 {
c.lock.RLock()
// Gather all the proposals that make sense voting on
addresses := make([]common.Address, 0, len(c.proposals))
for address, authorize := range c.proposals {
if snap.validVote(address, authorize) {
addresses = append(addresses, address)
}
}
// If there's pending proposals, cast a vote on them
if len(addresses) > 0 {
header.Coinbase = addresses[rand.Intn(len(addresses))]
if c.proposals[header.Coinbase] {
copy(header.Nonce[:], nonceAuthVote)
} else {
copy(header.Nonce[:], nonceDropVote)
}
}
c.lock.RUnlock()
}
// Set the correct difficulty // Set the correct difficulty
header.Difficulty = CalcDifficulty(snap, c.signer, c.config.ProducerInterval) header.Difficulty = CalcDifficulty(snap, c.signer, c.config.Epoch)
// Ensure the extra data has all it's components // Ensure the extra data has all it's components
if len(header.Extra) < extraVanity { if len(header.Extra) < extraVanity {
@ -604,10 +611,12 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
if parent == nil { if parent == nil {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor
} }
header.Time = parent.Time + c.config.Period + CalcProducerDelay(snap, c.config.ProducerInterval, c.config.ProducerDelay)
header.Time = parent.Time + CalcProducerDelay(snap, c.signer, c.config.Period, c.config.Epoch, c.config.ProducerDelay)
if header.Time < uint64(time.Now().Unix()) { if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix()) header.Time = uint64(time.Now().Unix())
} }
fmt.Println("parent.Time", parent.Time, "header.Time", header.Time, "number", header.Number)
return nil return nil
} }
@ -660,43 +669,52 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan
signer, signFn := c.signer, c.signFn signer, signFn := c.signer, c.signFn
c.lock.RUnlock() c.lock.RUnlock()
// Bail out if we're unauthorized to sign a block
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
if err != nil { if err != nil {
return err return err
} }
if _, authorized := snap.Signers[signer]; !authorized {
// Bail out if we're unauthorized to sign a block
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
return errUnauthorizedSigner return errUnauthorizedSigner
} }
// 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 validators := snap.ValidatorSet.Validators
if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit { // proposer will be the last signer if block is not epoch block
log.Info("Signed recently, must wait for others") proposer := snap.ValidatorSet.GetProposer().Address
// return nil if number%c.config.Epoch != 0 {
} // proposer = snap.Recents[number-1]
}
} }
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
limit := len(validators) - (len(validators)/2 + 1)
// temp index
tempIndex := signerIndex
if tempIndex < proposerIndex {
tempIndex = tempIndex + len(validators)
}
fmt.Println("Block temp index", "number", number, "tempIndex", tempIndex, "proposerIndex", proposerIndex)
if limit > 0 && tempIndex-proposerIndex > limit {
log.Info("Signed recently, must wait for others")
return nil
}
// Sweet, the protocol permits us to sign the block, wait for our time // Sweet, the protocol permits us to sign the block, wait for our time
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
if header.Difficulty.Cmp(diffNoTurn) == 0 { wiggle := time.Duration(wiggleTime) * time.Duration(tempIndex-proposerIndex)
// It's not our turn explicitly to sign, delay it a bit delay += wiggle
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
delay += time.Duration(rand.Int63n(int64(wiggle))) fmt.Println("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
fmt.Println("--> Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "signer", signer.Hex(), "proposer", proposer.Hex())
log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
}
// Sign all the things! // Sign all the things!
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header)) sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header))
if err != nil { if err != nil {
return err return err
} }
copy(header.Extra[len(header.Extra)-extraSeal:], sighash) copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
// Wait until sealing is terminated or delay timeout. // Wait until sealing is terminated or delay timeout.
log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay)) log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
go func() { go func() {
@ -724,7 +742,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *t
if err != nil { if err != nil {
return nil return nil
} }
return CalcDifficulty(snap, c.signer, c.config.ProducerInterval) return CalcDifficulty(snap, c.signer, c.config.Epoch)
} }
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.
@ -750,6 +768,11 @@ func (c *Bor) Close() error {
// GetCurrentValidators get current validators // GetCurrentValidators get current validators
func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) { func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) {
return GetValidators(number, c.config.ValidatorContract, c.ethAPI)
}
// GetValidators get current validators
func GetValidators(number uint64, validatorContract string, ethAPI *ethapi.PublicBlockChainAPI) ([]*Validator, error) {
blockNr := rpc.BlockNumber(number) blockNr := rpc.BlockNumber(number)
// validator set ABI // validator set ABI
@ -765,9 +788,9 @@ func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) {
// call // call
msgData := (hexutil.Bytes)(data) msgData := (hexutil.Bytes)(data)
toAddress := common.HexToAddress(c.config.ValidatorContract) toAddress := common.HexToAddress(validatorContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{ result, err := ethAPI.Call(ctx, ethapi.CallArgs{
Gas: &gas, Gas: &gas,
To: &toAddress, To: &toAddress,
Data: &msgData, Data: &msgData,
@ -800,3 +823,37 @@ func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) {
} }
return valz, nil return valz, nil
} }
func validatorContains(a []*Validator, x *Validator) (*Validator, bool) {
for _, n := range a {
if bytes.Compare(n.Address.Bytes(), x.Address.Bytes()) == 0 {
return n, true
}
}
return nil, false
}
func getUpdatedValidatorSet(oldValidatorSet *ValidatorSet, newVals []*Validator) *ValidatorSet {
v := oldValidatorSet
oldVals := v.Validators
var changes []*Validator
for _, ov := range oldVals {
if f, ok := validatorContains(newVals, ov); ok {
ov.VotingPower = f.VotingPower
} else {
ov.VotingPower = 0
}
changes = append(changes, ov)
}
for _, nv := range newVals {
if _, ok := validatorContains(changes, nv); !ok {
changes = append(changes, nv)
}
}
v.UpdateWithChangeSet(changes)
return v
}

View file

@ -19,11 +19,12 @@ package bor
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"sort" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
) )
@ -47,14 +48,15 @@ type Tally struct {
// Snapshot is the state of the authorization voting at a given point in time. // Snapshot is the state of the authorization voting at a given point in time.
type Snapshot struct { type Snapshot struct {
config *params.BorConfig // Consensus engine parameters to fine tune behavior config *params.BorConfig // Consensus engine parameters to fine tune behavior
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover ethAPI *ethapi.PublicBlockChainAPI
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
Number uint64 `json:"number"` // Block number where the snapshot was created Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment ValidatorSet *ValidatorSet `json:"validatorSet"` // Validator set at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
Votes []*Vote `json:"votes"` // List of votes cast in chronological order // Votes []*Vote `json:"votes"` // List of votes cast in chronological order
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating // Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
} }
// signersAscending implements the sort interface to allow sorting a list of addresses // signersAscending implements the sort interface to allow sorting a list of addresses
@ -67,24 +69,23 @@ func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// newSnapshot creates a new snapshot with the specified startup parameters. This // newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for // method does not initialize the set of recent signers, so only ever use if for
// the genesis block. // the genesis block.
func newSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot { func newSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, validators []*Validator, ethAPI *ethapi.PublicBlockChainAPI) *Snapshot {
snap := &Snapshot{ snap := &Snapshot{
config: config, config: config,
sigcache: sigcache, ethAPI: ethAPI,
Number: number, sigcache: sigcache,
Hash: hash, Number: number,
Signers: make(map[common.Address]struct{}), Hash: hash,
Recents: make(map[uint64]common.Address), ValidatorSet: NewValidatorSet(validators),
Tally: make(map[common.Address]Tally), Recents: make(map[uint64]common.Address),
} // Tally: make(map[common.Address]Tally),
for _, signer := range signers {
snap.Signers[signer] = struct{}{}
} }
fmt.Println("New validator set", "number", number, "proposer", snap.ValidatorSet.Proposer.Address.Hex())
return snap return snap
} }
// loadSnapshot loads an existing snapshot from the database. // loadSnapshot loads an existing snapshot from the database.
func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) { func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash, ethAPI *ethapi.PublicBlockChainAPI) (*Snapshot, error) {
blob, err := db.Get(append([]byte("bor-"), hash[:]...)) blob, err := db.Get(append([]byte("bor-"), hash[:]...))
if err != nil { if err != nil {
return nil, err return nil, err
@ -95,6 +96,7 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
} }
snap.config = config snap.config = config
snap.sigcache = sigcache snap.sigcache = sigcache
snap.ethAPI = ethAPI
return snap, nil return snap, nil
} }
@ -111,75 +113,71 @@ func (s *Snapshot) store(db ethdb.Database) error {
// copy creates a deep copy of the snapshot, though not the individual votes. // copy creates a deep copy of the snapshot, though not the individual votes.
func (s *Snapshot) copy() *Snapshot { func (s *Snapshot) copy() *Snapshot {
cpy := &Snapshot{ cpy := &Snapshot{
config: s.config, config: s.config,
sigcache: s.sigcache, ethAPI: s.ethAPI,
Number: s.Number, sigcache: s.sigcache,
Hash: s.Hash, Number: s.Number,
Signers: make(map[common.Address]struct{}), Hash: s.Hash,
Recents: make(map[uint64]common.Address), ValidatorSet: s.ValidatorSet.Copy(),
Votes: make([]*Vote, len(s.Votes)), Recents: make(map[uint64]common.Address),
Tally: make(map[common.Address]Tally), // Votes: make([]*Vote, len(s.Votes)),
} // Tally: make(map[common.Address]Tally),
for signer := range s.Signers {
cpy.Signers[signer] = struct{}{}
} }
for block, signer := range s.Recents { for block, signer := range s.Recents {
cpy.Recents[block] = signer cpy.Recents[block] = signer
} }
for address, tally := range s.Tally { // for address, tally := range s.Tally {
cpy.Tally[address] = tally // cpy.Tally[address] = tally
} // }
copy(cpy.Votes, s.Votes) // copy(cpy.Votes, s.Votes)
return cpy return cpy
} }
// validVote returns whether it makes sense to cast the specified vote in the // // validVote returns whether it makes sense to cast the specified vote in the
// given snapshot context (e.g. don't try to add an already authorized signer). // // given snapshot context (e.g. don't try to add an already authorized signer).
func (s *Snapshot) validVote(address common.Address, authorize bool) bool { // func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
_, signer := s.Signers[address] // _, signer := s.Signers[address]
return (signer && !authorize) || (!signer && authorize) // return (signer && !authorize) || (!signer && authorize)
} // }
// cast adds a new vote into the tally. // // cast adds a new vote into the tally.
func (s *Snapshot) cast(address common.Address, authorize bool) bool { // func (s *Snapshot) cast(address common.Address, authorize bool) bool {
// Ensure the vote is meaningful // // Ensure the vote is meaningful
if !s.validVote(address, authorize) { // if !s.validVote(address, authorize) {
return false // return false
} // }
// Cast the vote into an existing or new tally // // Cast the vote into an existing or new tally
if old, ok := s.Tally[address]; ok { // if old, ok := s.Tally[address]; ok {
old.Votes++ // old.Votes++
s.Tally[address] = old // s.Tally[address] = old
} else { // } else {
s.Tally[address] = Tally{Authorize: authorize, Votes: 1} // s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
} // }
return true // return true
} // }
// uncast removes a previously cast vote from the tally. // // uncast removes a previously cast vote from the tally.
func (s *Snapshot) uncast(address common.Address, authorize bool) bool { // func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
// If there's no tally, it's a dangling vote, just drop // // If there's no tally, it's a dangling vote, just drop
tally, ok := s.Tally[address] // tally, ok := s.Tally[address]
if !ok { // if !ok {
return false // return false
} // }
// Ensure we only revert counted votes // // Ensure we only revert counted votes
if tally.Authorize != authorize { // if tally.Authorize != authorize {
return false // return false
} // }
// Otherwise revert the vote // // Otherwise revert the vote
if tally.Votes > 1 { // if tally.Votes > 1 {
tally.Votes-- // tally.Votes--
s.Tally[address] = tally // s.Tally[address] = tally
} else { // } else {
delete(s.Tally, address) // delete(s.Tally, address)
} // }
return true // return true
} // }
// apply creates a new authorization snapshot by applying the given headers to
// the original one.
func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// Allow passing in no headers for cleaner code // Allow passing in no headers for cleaner code
if len(headers) == 0 { if len(headers) == 0 {
@ -188,11 +186,11 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// Sanity check that the headers can be applied // Sanity check that the headers can be applied
for i := 0; i < len(headers)-1; i++ { for i := 0; i < len(headers)-1; i++ {
if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
return nil, errInvalidVotingChain return nil, errOutOfRangeChain
} }
} }
if headers[0].Number.Uint64() != s.Number+1 { if headers[0].Number.Uint64() != s.Number+1 {
return nil, errInvalidVotingChain return nil, errOutOfRangeChain
} }
// Iterate through the headers and create a new snapshot // Iterate through the headers and create a new snapshot
snap := s.copy() snap := s.copy()
@ -200,91 +198,125 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
for _, header := range headers { for _, header := range headers {
// Remove any votes on checkpoint blocks // Remove any votes on checkpoint blocks
number := header.Number.Uint64() number := header.Number.Uint64()
if number%s.config.Epoch == 0 { if (number+1)%s.config.Epoch == 0 {
snap.Votes = nil // snap.Votes = nil
snap.Tally = make(map[common.Address]Tally) // snap.Tally = make(map[common.Address]Tally)
} }
// Delete the oldest signer from the recent list to allow it signing again // Delete the oldest signer from the recent list to allow it signing again
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { if number >= s.config.Epoch && number-s.config.Epoch >= 0 {
delete(snap.Recents, number-limit) delete(snap.Recents, number-s.config.Epoch)
} }
// Resolve the authorization key and check against signers // Resolve the authorization key and check against signers
signer, err := ecrecover(header, s.sigcache) signer, err := ecrecover(header, s.sigcache)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if _, ok := snap.Signers[signer]; !ok {
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
return nil, errUnauthorizedSigner return nil, errUnauthorizedSigner
} }
for _, recent := range snap.Recents {
if recent == signer { //
// return nil, errRecentlySigned // Check validator
//
validators := snap.ValidatorSet.Validators
// proposer will be the last signer if block is not epoch block
proposer := snap.ValidatorSet.GetProposer().Address
// if number%s.config.Epoch != 0 {
// proposer = snap.Recents[number-1]
// }
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
limit := len(validators) - (len(validators)/2 + 1)
// temp index
tempIndex := signerIndex
if proposerIndex != tempIndex && limit > 0 {
if tempIndex < proposerIndex {
tempIndex = tempIndex + len(validators)
}
if tempIndex-proposerIndex > limit {
return nil, errRecentlySigned
} }
} }
// add recents
snap.Recents[number] = signer snap.Recents[number] = signer
fmt.Println("Recent signer", "number", number, "signer", signer.Hex())
// Header authorized, discard any previous votes from the signer // change proposer on epoch
for i, vote := range snap.Votes { if number > 0 && (number+1)%s.config.Epoch == 0 {
if vote.Signer == signer && vote.Address == header.Coinbase { newVals, _ := GetValidators(number, snap.config.ValidatorContract, snap.ethAPI)
// Uncast the vote from the cached tally v := getUpdatedValidatorSet(snap.ValidatorSet.Copy(), newVals)
snap.uncast(vote.Address, vote.Authorize) v.IncrementProposerPriority(1)
snap.ValidatorSet = v
// Uncast the vote from the chronological list fmt.Println("New validator set", "number", number, "proposer", v.Proposer.Address.Hex())
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
break // only one vote allowed
}
} }
// // Header authorized, discard any previous votes from the signer
// for i, vote := range snap.Votes {
// if vote.Signer == signer && vote.Address == header.Coinbase {
// // Uncast the vote from the cached tally
// snap.uncast(vote.Address, vote.Authorize)
// // Uncast the vote from the chronological list
// snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
// break // only one vote allowed
// }
// }
// Tally up the new vote from the signer // Tally up the new vote from the signer
var authorize bool // var authorize bool
switch { // switch {
case bytes.Equal(header.Nonce[:], nonceAuthVote): // case bytes.Equal(header.Nonce[:], nonceAuthVote):
authorize = true // authorize = true
case bytes.Equal(header.Nonce[:], nonceDropVote): // case bytes.Equal(header.Nonce[:], nonceDropVote):
authorize = false // authorize = false
default: // default:
return nil, errInvalidVote // return nil, errInvalidVote
} // }
if snap.cast(header.Coinbase, authorize) { // if snap.cast(header.Coinbase, authorize) {
snap.Votes = append(snap.Votes, &Vote{ // snap.Votes = append(snap.Votes, &Vote{
Signer: signer, // Signer: signer,
Block: number, // Block: number,
Address: header.Coinbase, // Address: header.Coinbase,
Authorize: authorize, // Authorize: authorize,
}) // })
} // }
// If the vote passed, update the list of signers // If the vote passed, update the list of signers
if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 { // if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
if tally.Authorize { // if tally.Authorize {
snap.Signers[header.Coinbase] = struct{}{} // snap.Signers[header.Coinbase] = struct{}{}
} else { // } else {
delete(snap.Signers, header.Coinbase) // delete(snap.Signers, header.Coinbase)
// Signer list shrunk, delete any leftover recent caches // // Signer list shrunk, delete any leftover recent caches
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { // if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
delete(snap.Recents, number-limit) // delete(snap.Recents, number-limit)
} // }
// Discard any previous votes the deauthorized signer cast // // Discard any previous votes the deauthorized signer cast
for i := 0; i < len(snap.Votes); i++ { // for i := 0; i < len(snap.Votes); i++ {
if snap.Votes[i].Signer == header.Coinbase { // if snap.Votes[i].Signer == header.Coinbase {
// Uncast the vote from the cached tally // // Uncast the vote from the cached tally
snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) // snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
// Uncast the vote from the chronological list // // Uncast the vote from the chronological list
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) // snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
i-- // i--
} // }
} // }
} // }
// Discard any previous votes around the just changed account // // Discard any previous votes around the just changed account
for i := 0; i < len(snap.Votes); i++ { // for i := 0; i < len(snap.Votes); i++ {
if snap.Votes[i].Address == header.Coinbase { // if snap.Votes[i].Address == header.Coinbase {
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) // snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
i-- // i--
} // }
} // }
delete(snap.Tally, header.Coinbase) // delete(snap.Tally, header.Coinbase)
} // }
} }
snap.Number += uint64(len(headers)) snap.Number += uint64(len(headers))
snap.Hash = headers[len(headers)-1].Hash() snap.Hash = headers[len(headers)-1].Hash()
@ -294,19 +326,54 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
// signers retrieves the list of authorized signers in ascending order. // signers retrieves the list of authorized signers in ascending order.
func (s *Snapshot) signers() []common.Address { func (s *Snapshot) signers() []common.Address {
sigs := make([]common.Address, 0, len(s.Signers)) sigs := make([]common.Address, 0, len(s.ValidatorSet.Validators))
for sig := range s.Signers { for _, sig := range s.ValidatorSet.Validators {
sigs = append(sigs, sig) sigs = append(sigs, sig.Address)
} }
sort.Sort(signersAscending(sigs))
return sigs return sigs
} }
// inturn returns if a signer at a given block height is in-turn or not. // inturn returns if a signer at a given block height is in-turn or not.
func (s *Snapshot) inturn(number uint64, signer common.Address, producerPeriod uint64) bool { func (s *Snapshot) inturn(number uint64, signer common.Address, epoch uint64) uint64 {
signers, offset := s.signers(), 0 // if signer is empty
for offset < len(signers) && signers[offset] != signer { if bytes.Compare(signer.Bytes(), common.Address{}.Bytes()) == 0 {
offset++ return 1
} }
return ((number / producerPeriod) % uint64(len(signers))) == uint64(offset)
validators := s.ValidatorSet.Validators
proposer := s.ValidatorSet.GetProposer().Address
totalValidators := len(validators)
// proposer will be the last signer if block is not epoch block
// proposer := snap.ValidatorSet.GetProposer().Address
// if number%epoch != 0 {
// proposer = snap.Recents[number-1]
// }
proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer)
signerIndex, _ := s.ValidatorSet.GetByAddress(signer)
// temp index
tempIndex := signerIndex
if tempIndex < proposerIndex {
tempIndex = tempIndex + totalValidators
}
return uint64(totalValidators - (tempIndex - proposerIndex))
// signers, offset := s.signers(), 0
// for offset < len(signers) && signers[offset] != signer {
// offset++
// }
// return ((number / producerPeriod) % uint64(len(signers))) == uint64(offset)
// // if block is epoch start block, proposer will be inturn signer
// if s.Number%epoch == 0 {
// if bytes.Compare(proposer.Address.Bytes(), signer.Bytes()) == 0 {
// return true
// }
// // if block is not epoch block, last block signer will be inturn
// } else if bytes.Compare(lastSigner.Bytes(), signer.Bytes()) == 0 {
// return false
// }
// return false
} }

View file

@ -228,7 +228,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
// The first thing the node will do is reconstruct the verification data for // The first thing the node will do is reconstruct the verification data for
// the head block (ethash cache or clique voting snapshot). Might as well do // the head block (ethash cache or clique voting snapshot). Might as well do
// it in advance. // it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader(), true) // bc.engine.VerifyHeader(bc, bc.CurrentHeader(), true)
if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 { if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
var ( var (

View file

@ -183,7 +183,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
TrieTimeLimit: config.TrieTimeout, TrieTimeLimit: config.TrieTimeout,
} }
) )
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve)
eth.engine.VerifyHeader(eth.blockchain, eth.blockchain.CurrentHeader(), true) // TODO think on it
if err != nil { if err != nil {
return nil, err return nil, err
} }