mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
fix block time bug
This commit is contained in:
parent
ad198aeefd
commit
f12ae309cf
4 changed files with 381 additions and 254 deletions
|
|
@ -8,7 +8,6 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -39,7 +38,7 @@ const (
|
|||
inmemorySnapshots = 128 // Number of recent vote snapshots 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.
|
||||
|
|
@ -116,9 +115,9 @@ var (
|
|||
// the previous block's timestamp + the minimum block period.
|
||||
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.
|
||||
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 = 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
|
||||
// that a new block should have based on the previous blocks in the chain and the
|
||||
// current signer.
|
||||
func CalcDifficulty(snap *Snapshot, signer common.Address, producerPeriod uint64) *big.Int {
|
||||
if snap.inturn(snap.Number+1, signer, producerPeriod) {
|
||||
return new(big.Int).Set(diffInTurn)
|
||||
}
|
||||
return new(big.Int).Set(diffNoTurn)
|
||||
func CalcDifficulty(snap *Snapshot, signer common.Address, epoch uint64) *big.Int {
|
||||
return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, epoch))
|
||||
}
|
||||
|
||||
// CalcProducerDelay is the producer delay algorithm based on block time.
|
||||
func CalcProducerDelay(snap *Snapshot, producerInterval uint64, producerDelay uint64) uint64 {
|
||||
isFirstBlock := (snap.Number + 1) % producerInterval
|
||||
if isFirstBlock == 0 {
|
||||
func CalcProducerDelay(snap *Snapshot, signer common.Address, period uint64, epoch uint64, producerDelay uint64) uint64 {
|
||||
// lastSigner := snap.Recents[snap.Number]
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
|
@ -235,7 +239,8 @@ type Bor struct {
|
|||
signFn SignerFn // Signer function to authorize hashes with
|
||||
lock sync.RWMutex // Protects the signer fields
|
||||
|
||||
ethAPI *ethapi.PublicBlockChainAPI
|
||||
ethAPI *ethapi.PublicBlockChainAPI
|
||||
validatorSetABI abi.ABI
|
||||
|
||||
// The fields below are for testing only
|
||||
fakeDiff bool // Skip difficulty verifications
|
||||
|
|
@ -248,18 +253,21 @@ func New(config *params.BorConfig, db ethdb.Database, ethAPI *ethapi.PublicBlock
|
|||
if conf.Epoch == 0 {
|
||||
conf.Epoch = epochLength
|
||||
}
|
||||
|
||||
// Allocate the snapshot caches and create the engine
|
||||
recents, _ := lru.NewARC(inmemorySnapshots)
|
||||
signatures, _ := lru.NewARC(inmemorySignatures)
|
||||
|
||||
return &Bor{
|
||||
config: &conf,
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recents: recents,
|
||||
signatures: signatures,
|
||||
proposals: make(map[common.Address]bool),
|
||||
validatorSetABI, _ := abi.JSON(strings.NewReader(validatorset.ValidatorsetABI))
|
||||
c := &Bor{
|
||||
config: &conf,
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recents: recents,
|
||||
signatures: signatures,
|
||||
proposals: make(map[common.Address]bool),
|
||||
validatorSetABI: validatorSetABI,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// If the block is a checkpoint block, verify the signer list
|
||||
// TODO verify signers
|
||||
if number%c.config.Epoch == 0 {
|
||||
signers := make([]byte, len(snap.Signers)*common.AddressLength)
|
||||
for i, signer := range snap.signers() {
|
||||
copy(signers[i*common.AddressLength:], signer[:])
|
||||
}
|
||||
extraSuffix := len(header.Extra) - extraSeal
|
||||
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
|
||||
return errMismatchingCheckpointSigners
|
||||
}
|
||||
// signers := make([]byte, len(snap.Signers)*common.AddressLength)
|
||||
// for i, signer := range snap.signers() {
|
||||
// copy(signers[i*common.AddressLength:], signer[:])
|
||||
// }
|
||||
// extraSuffix := len(header.Extra) - extraSeal
|
||||
// if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
|
||||
// return errMismatchingCheckpointSigners
|
||||
// }
|
||||
}
|
||||
// All basic checks passed, verify the seal and return
|
||||
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 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)
|
||||
snap = s
|
||||
break
|
||||
|
|
@ -428,15 +438,19 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H
|
|||
if checkpoint != nil {
|
||||
hash := checkpoint.Hash()
|
||||
|
||||
// validatorSet := new(ValidatorSet)
|
||||
// TODO populate validator set
|
||||
c.GetCurrentValidators(number)
|
||||
// current validators
|
||||
validators, err := c.GetCurrentValidators(number)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// TODO remove signer
|
||||
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:])
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -519,30 +533,43 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := snap.Signers[signer]; !ok {
|
||||
|
||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
||||
return errUnauthorizedSigner
|
||||
}
|
||||
for seen, recent := range snap.Recents {
|
||||
if recent == signer {
|
||||
// Signer is among recents, only fail if the current block doesn't shift it out
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
|
||||
// return errRecentlySigned
|
||||
}
|
||||
|
||||
// check if signer is correct
|
||||
validators := snap.ValidatorSet.Validators
|
||||
// proposer will be the last signer if block is not epoch block
|
||||
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
|
||||
if !c.fakeDiff {
|
||||
inturn := snap.inturn(header.Number.Uint64(), signer, c.config.ProducerInterval)
|
||||
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
|
||||
return errWrongDifficulty
|
||||
}
|
||||
if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
|
||||
difficulty := snap.inturn(header.Number.Uint64(), signer, c.config.Epoch)
|
||||
if header.Difficulty.Uint64() != difficulty {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -559,29 +586,9 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
|||
if err != nil {
|
||||
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
|
||||
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
|
||||
if len(header.Extra) < extraVanity {
|
||||
|
|
@ -604,10 +611,12 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
|||
if parent == nil {
|
||||
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()) {
|
||||
header.Time = uint64(time.Now().Unix())
|
||||
}
|
||||
fmt.Println("parent.Time", parent.Time, "header.Time", header.Time, "number", header.Number)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -660,43 +669,52 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan
|
|||
signer, signFn := c.signer, c.signFn
|
||||
c.lock.RUnlock()
|
||||
|
||||
// Bail out if we're unauthorized to sign a block
|
||||
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
// 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")
|
||||
// return nil
|
||||
}
|
||||
}
|
||||
validators := snap.ValidatorSet.Validators
|
||||
// proposer will be the last signer if block is not epoch block
|
||||
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 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
|
||||
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
|
||||
if header.Difficulty.Cmp(diffNoTurn) == 0 {
|
||||
// It's not our turn explicitly to sign, delay it a bit
|
||||
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
|
||||
delay += time.Duration(rand.Int63n(int64(wiggle)))
|
||||
wiggle := time.Duration(wiggleTime) * time.Duration(tempIndex-proposerIndex)
|
||||
delay += 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!
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
|
||||
|
||||
// Wait until sealing is terminated or delay timeout.
|
||||
log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
|
||||
go func() {
|
||||
|
|
@ -724,7 +742,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *t
|
|||
if err != 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.
|
||||
|
|
@ -750,6 +768,11 @@ func (c *Bor) Close() error {
|
|||
|
||||
// GetCurrentValidators get current validators
|
||||
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)
|
||||
|
||||
// validator set ABI
|
||||
|
|
@ -765,9 +788,9 @@ func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) {
|
|||
|
||||
// call
|
||||
msgData := (hexutil.Bytes)(data)
|
||||
toAddress := common.HexToAddress(c.config.ValidatorContract)
|
||||
toAddress := common.HexToAddress(validatorContract)
|
||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
result, err := ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
Gas: &gas,
|
||||
To: &toAddress,
|
||||
Data: &msgData,
|
||||
|
|
@ -800,3 +823,37 @@ func (c *Bor) GetCurrentValidators(number uint64) ([]*Validator, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@ package bor
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
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.
|
||||
type Snapshot struct {
|
||||
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
|
||||
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
|
||||
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
|
||||
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
|
||||
Number uint64 `json:"number"` // Block number where the snapshot was created
|
||||
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
||||
ValidatorSet *ValidatorSet `json:"validatorSet"` // Validator set at this moment
|
||||
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
|
||||
// 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
|
||||
|
|
@ -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
|
||||
// method does not initialize the set of recent signers, so only ever use if for
|
||||
// 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{
|
||||
config: config,
|
||||
sigcache: sigcache,
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
Signers: make(map[common.Address]struct{}),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
for _, signer := range signers {
|
||||
snap.Signers[signer] = struct{}{}
|
||||
config: config,
|
||||
ethAPI: ethAPI,
|
||||
sigcache: sigcache,
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
ValidatorSet: NewValidatorSet(validators),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
// Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
fmt.Println("New validator set", "number", number, "proposer", snap.ValidatorSet.Proposer.Address.Hex())
|
||||
return snap
|
||||
}
|
||||
|
||||
// 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[:]...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -95,6 +96,7 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
|
|||
}
|
||||
snap.config = config
|
||||
snap.sigcache = sigcache
|
||||
snap.ethAPI = ethAPI
|
||||
|
||||
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.
|
||||
func (s *Snapshot) copy() *Snapshot {
|
||||
cpy := &Snapshot{
|
||||
config: s.config,
|
||||
sigcache: s.sigcache,
|
||||
Number: s.Number,
|
||||
Hash: s.Hash,
|
||||
Signers: make(map[common.Address]struct{}),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
Votes: make([]*Vote, len(s.Votes)),
|
||||
Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
for signer := range s.Signers {
|
||||
cpy.Signers[signer] = struct{}{}
|
||||
config: s.config,
|
||||
ethAPI: s.ethAPI,
|
||||
sigcache: s.sigcache,
|
||||
Number: s.Number,
|
||||
Hash: s.Hash,
|
||||
ValidatorSet: s.ValidatorSet.Copy(),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
// Votes: make([]*Vote, len(s.Votes)),
|
||||
// Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
for block, signer := range s.Recents {
|
||||
cpy.Recents[block] = signer
|
||||
}
|
||||
for address, tally := range s.Tally {
|
||||
cpy.Tally[address] = tally
|
||||
}
|
||||
copy(cpy.Votes, s.Votes)
|
||||
// for address, tally := range s.Tally {
|
||||
// cpy.Tally[address] = tally
|
||||
// }
|
||||
// copy(cpy.Votes, s.Votes)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// 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).
|
||||
func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
|
||||
_, signer := s.Signers[address]
|
||||
return (signer && !authorize) || (!signer && authorize)
|
||||
}
|
||||
// // 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).
|
||||
// func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
|
||||
// _, signer := s.Signers[address]
|
||||
// return (signer && !authorize) || (!signer && authorize)
|
||||
// }
|
||||
|
||||
// cast adds a new vote into the tally.
|
||||
func (s *Snapshot) cast(address common.Address, authorize bool) bool {
|
||||
// Ensure the vote is meaningful
|
||||
if !s.validVote(address, authorize) {
|
||||
return false
|
||||
}
|
||||
// Cast the vote into an existing or new tally
|
||||
if old, ok := s.Tally[address]; ok {
|
||||
old.Votes++
|
||||
s.Tally[address] = old
|
||||
} else {
|
||||
s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
|
||||
}
|
||||
return true
|
||||
}
|
||||
// // cast adds a new vote into the tally.
|
||||
// func (s *Snapshot) cast(address common.Address, authorize bool) bool {
|
||||
// // Ensure the vote is meaningful
|
||||
// if !s.validVote(address, authorize) {
|
||||
// return false
|
||||
// }
|
||||
// // Cast the vote into an existing or new tally
|
||||
// if old, ok := s.Tally[address]; ok {
|
||||
// old.Votes++
|
||||
// s.Tally[address] = old
|
||||
// } else {
|
||||
// s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
|
||||
// uncast removes a previously cast vote from the tally.
|
||||
func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
|
||||
// If there's no tally, it's a dangling vote, just drop
|
||||
tally, ok := s.Tally[address]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Ensure we only revert counted votes
|
||||
if tally.Authorize != authorize {
|
||||
return false
|
||||
}
|
||||
// Otherwise revert the vote
|
||||
if tally.Votes > 1 {
|
||||
tally.Votes--
|
||||
s.Tally[address] = tally
|
||||
} else {
|
||||
delete(s.Tally, address)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// // uncast removes a previously cast vote from the tally.
|
||||
// func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
|
||||
// // If there's no tally, it's a dangling vote, just drop
|
||||
// tally, ok := s.Tally[address]
|
||||
// if !ok {
|
||||
// return false
|
||||
// }
|
||||
// // Ensure we only revert counted votes
|
||||
// if tally.Authorize != authorize {
|
||||
// return false
|
||||
// }
|
||||
// // Otherwise revert the vote
|
||||
// if tally.Votes > 1 {
|
||||
// tally.Votes--
|
||||
// s.Tally[address] = tally
|
||||
// } else {
|
||||
// delete(s.Tally, address)
|
||||
// }
|
||||
// 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) {
|
||||
// Allow passing in no headers for cleaner code
|
||||
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
|
||||
for i := 0; i < len(headers)-1; i++ {
|
||||
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 {
|
||||
return nil, errInvalidVotingChain
|
||||
return nil, errOutOfRangeChain
|
||||
}
|
||||
// Iterate through the headers and create a new snapshot
|
||||
snap := s.copy()
|
||||
|
|
@ -200,91 +198,125 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
|||
for _, header := range headers {
|
||||
// Remove any votes on checkpoint blocks
|
||||
number := header.Number.Uint64()
|
||||
if number%s.config.Epoch == 0 {
|
||||
snap.Votes = nil
|
||||
snap.Tally = make(map[common.Address]Tally)
|
||||
if (number+1)%s.config.Epoch == 0 {
|
||||
// snap.Votes = nil
|
||||
// snap.Tally = make(map[common.Address]Tally)
|
||||
}
|
||||
|
||||
// Delete the oldest signer from the recent list to allow it signing again
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
|
||||
delete(snap.Recents, number-limit)
|
||||
if number >= s.config.Epoch && number-s.config.Epoch >= 0 {
|
||||
delete(snap.Recents, number-s.config.Epoch)
|
||||
}
|
||||
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, s.sigcache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := snap.Signers[signer]; !ok {
|
||||
|
||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
fmt.Println("Recent signer", "number", number, "signer", signer.Hex())
|
||||
// change proposer on epoch
|
||||
if number > 0 && (number+1)%s.config.Epoch == 0 {
|
||||
newVals, _ := GetValidators(number, snap.config.ValidatorContract, snap.ethAPI)
|
||||
v := getUpdatedValidatorSet(snap.ValidatorSet.Copy(), newVals)
|
||||
v.IncrementProposerPriority(1)
|
||||
snap.ValidatorSet = v
|
||||
fmt.Println("New validator set", "number", number, "proposer", v.Proposer.Address.Hex())
|
||||
}
|
||||
// // 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
|
||||
var authorize bool
|
||||
switch {
|
||||
case bytes.Equal(header.Nonce[:], nonceAuthVote):
|
||||
authorize = true
|
||||
case bytes.Equal(header.Nonce[:], nonceDropVote):
|
||||
authorize = false
|
||||
default:
|
||||
return nil, errInvalidVote
|
||||
}
|
||||
if snap.cast(header.Coinbase, authorize) {
|
||||
snap.Votes = append(snap.Votes, &Vote{
|
||||
Signer: signer,
|
||||
Block: number,
|
||||
Address: header.Coinbase,
|
||||
Authorize: authorize,
|
||||
})
|
||||
}
|
||||
// var authorize bool
|
||||
// switch {
|
||||
// case bytes.Equal(header.Nonce[:], nonceAuthVote):
|
||||
// authorize = true
|
||||
// case bytes.Equal(header.Nonce[:], nonceDropVote):
|
||||
// authorize = false
|
||||
// default:
|
||||
// return nil, errInvalidVote
|
||||
// }
|
||||
// if snap.cast(header.Coinbase, authorize) {
|
||||
// snap.Votes = append(snap.Votes, &Vote{
|
||||
// Signer: signer,
|
||||
// Block: number,
|
||||
// Address: header.Coinbase,
|
||||
// Authorize: authorize,
|
||||
// })
|
||||
// }
|
||||
// If the vote passed, update the list of signers
|
||||
if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
|
||||
if tally.Authorize {
|
||||
snap.Signers[header.Coinbase] = struct{}{}
|
||||
} else {
|
||||
delete(snap.Signers, header.Coinbase)
|
||||
// if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
|
||||
// if tally.Authorize {
|
||||
// snap.Signers[header.Coinbase] = struct{}{}
|
||||
// } else {
|
||||
// delete(snap.Signers, header.Coinbase)
|
||||
|
||||
// Signer list shrunk, delete any leftover recent caches
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
|
||||
delete(snap.Recents, number-limit)
|
||||
}
|
||||
// Discard any previous votes the deauthorized signer cast
|
||||
for i := 0; i < len(snap.Votes); i++ {
|
||||
if snap.Votes[i].Signer == header.Coinbase {
|
||||
// Uncast the vote from the cached tally
|
||||
snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
|
||||
// // Signer list shrunk, delete any leftover recent caches
|
||||
// if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
|
||||
// delete(snap.Recents, number-limit)
|
||||
// }
|
||||
// // Discard any previous votes the deauthorized signer cast
|
||||
// for i := 0; i < len(snap.Votes); i++ {
|
||||
// if snap.Votes[i].Signer == header.Coinbase {
|
||||
// // Uncast the vote from the cached tally
|
||||
// snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
|
||||
|
||||
// Uncast the vote from the chronological list
|
||||
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
// // Uncast the vote from the chronological list
|
||||
// snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
// Discard any previous votes around the just changed account
|
||||
for i := 0; i < len(snap.Votes); i++ {
|
||||
if snap.Votes[i].Address == header.Coinbase {
|
||||
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
delete(snap.Tally, header.Coinbase)
|
||||
}
|
||||
// i--
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // Discard any previous votes around the just changed account
|
||||
// for i := 0; i < len(snap.Votes); i++ {
|
||||
// if snap.Votes[i].Address == header.Coinbase {
|
||||
// snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
// i--
|
||||
// }
|
||||
// }
|
||||
// delete(snap.Tally, header.Coinbase)
|
||||
// }
|
||||
}
|
||||
snap.Number += uint64(len(headers))
|
||||
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.
|
||||
func (s *Snapshot) signers() []common.Address {
|
||||
sigs := make([]common.Address, 0, len(s.Signers))
|
||||
for sig := range s.Signers {
|
||||
sigs = append(sigs, sig)
|
||||
sigs := make([]common.Address, 0, len(s.ValidatorSet.Validators))
|
||||
for _, sig := range s.ValidatorSet.Validators {
|
||||
sigs = append(sigs, sig.Address)
|
||||
}
|
||||
sort.Sort(signersAscending(sigs))
|
||||
return sigs
|
||||
}
|
||||
|
||||
// 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 {
|
||||
signers, offset := s.signers(), 0
|
||||
for offset < len(signers) && signers[offset] != signer {
|
||||
offset++
|
||||
func (s *Snapshot) inturn(number uint64, signer common.Address, epoch uint64) uint64 {
|
||||
// if signer is empty
|
||||
if bytes.Compare(signer.Bytes(), common.Address{}.Bytes()) == 0 {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 head block (ethash cache or clique voting snapshot). Might as well do
|
||||
// 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 {
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -183,7 +183,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
TrieTimeLimit: config.TrieTimeout,
|
||||
}
|
||||
)
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue