github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/consensus/podc/backend/snapshot.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backend
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus/podc"
    24  	"github.com/ethereum/go-ethereum/consensus/podc/validator"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/ethdb"
    27  )
    28  
    29  const (
    30  	dbKeySnapshotPrefix = "podc-snapshot"
    31  )
    32  
    33  // Vote represents a single vote that an authorized validator made to modify the
    34  // list of authorizations.
    35  type Vote struct {
    36  	Validator common.Address `json:"validator"` // Authorized validator that cast this vote
    37  	Block     uint64         `json:"block"`     // Block number the vote was cast in (expire old votes)
    38  	Address   common.Address `json:"address"`   // Account being voted on to change its authorization
    39  	Authorize bool           `json:"authorize"` // Whether to authorize or deauthorize the voted account
    40  }
    41  
    42  // Tally is a simple vote tally to keep the current score of votes. Votes that
    43  // go against the proposal aren't counted since it's equivalent to not voting.
    44  type Tally struct {
    45  	Authorize bool `json:"authorize"` // Whether the vote it about authorizing or kicking someone
    46  	Votes     int  `json:"votes"`     // Number of votes until now wanting to pass the proposal
    47  }
    48  
    49  type Snapshot struct {
    50  	Epoch uint64 // The number of blocks after which to checkpoint and reset the pending votes
    51  
    52  	Number uint64                   `json:"number"`     // Block number where the snapshot was created
    53  	Hash   common.Hash              `json:"hash"`       // Block hash where the snapshot was created
    54  	Votes  []*Vote                  `json:"votes"`      // List of votes cast in chronological order
    55  	Tally  map[common.Address]Tally `json:"tally"`      // Current vote tally to avoid recalculating
    56  	ValSet podc.ValidatorSet        `json:"validators"` // Set of authorized validators at this moment
    57  }
    58  
    59  // newSnapshot create a new snapshot with the specified startup parameters. This
    60  // method does not initialize the set of recent validators, so only ever use if for
    61  // the genesis block.
    62  func newSnapshot(epoch uint64, number uint64, hash common.Hash, valSet podc.ValidatorSet) *Snapshot {
    63  	snap := &Snapshot{
    64  		Epoch:  epoch,
    65  		Number: number,
    66  		Hash:   hash,
    67  		ValSet: valSet,
    68  		Tally:  make(map[common.Address]Tally),
    69  	}
    70  	return snap
    71  }
    72  
    73  func loadSnapshot(epoch uint64, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
    74  	blob, err := db.Get(append([]byte(dbKeySnapshotPrefix), hash[:]...))
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	snap := new(Snapshot)
    79  	var addrs []common.Address
    80  	snap.ValSet = validator.NewSet(addrs, podc.RoundRobin)
    81  	if err := json.Unmarshal(blob, snap); err != nil {
    82  		return nil, err
    83  	}
    84  	snap.Epoch = epoch
    85  
    86  	return snap, nil
    87  }
    88  
    89  // store inserts the snapshot into the database.
    90  func (s *Snapshot) store(db ethdb.Database) error {
    91  	blob, err := json.Marshal(s)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	return db.Put(append([]byte(dbKeySnapshotPrefix), s.Hash[:]...), blob)
    96  }
    97  
    98  // copy creates a deep copy of the snapshot, though not the individual votes.
    99  func (s *Snapshot) copy() *Snapshot {
   100  	cpy := &Snapshot{
   101  		Epoch:  s.Epoch,
   102  		Number: s.Number,
   103  		Hash:   s.Hash,
   104  		ValSet: s.ValSet.Copy(),
   105  		Votes:  make([]*Vote, len(s.Votes)),
   106  		Tally:  make(map[common.Address]Tally),
   107  	}
   108  
   109  	for address, tally := range s.Tally {
   110  		cpy.Tally[address] = tally
   111  	}
   112  	copy(cpy.Votes, s.Votes)
   113  
   114  	return cpy
   115  }
   116  
   117  // checkVote return whether it's a valid vote
   118  func (s *Snapshot) checkVote(address common.Address, authorize bool) bool {
   119  	_, validator := s.ValSet.GetByAddress(address)
   120  	return (validator != nil && !authorize) || (validator == nil && authorize)
   121  }
   122  
   123  // cast adds a new vote into the tally.
   124  func (s *Snapshot) cast(address common.Address, authorize bool) bool {
   125  	// Ensure the vote is meaningful
   126  	if !s.checkVote(address, authorize) {
   127  		return false
   128  	}
   129  	// Cast the vote into an existing or new tally
   130  	if old, ok := s.Tally[address]; ok {
   131  		old.Votes++
   132  		s.Tally[address] = old
   133  	} else {
   134  		s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
   135  	}
   136  	return true
   137  }
   138  
   139  // uncast removes a previously cast vote from the tally.
   140  func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
   141  	// If there's no tally, it's a dangling vote, just drop
   142  	tally, ok := s.Tally[address]
   143  	if !ok {
   144  		return false
   145  	}
   146  	// Ensure we only revert counted votes
   147  	if tally.Authorize != authorize {
   148  		return false
   149  	}
   150  	// Otherwise revert the vote
   151  	if tally.Votes > 1 {
   152  		tally.Votes--
   153  		s.Tally[address] = tally
   154  	} else {
   155  		delete(s.Tally, address)
   156  	}
   157  	return true
   158  }
   159  
   160  // apply creates a new authorization snapshot by applying the given headers to
   161  // the original one.
   162  func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
   163  	// Allow passing in no headers for cleaner code
   164  	if len(headers) == 0 {
   165  		return s, nil
   166  	}
   167  	// Sanity check that the headers can be applied
   168  	for i := 0; i < len(headers)-1; i++ {
   169  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
   170  			return nil, errInvalidVotingChain
   171  		}
   172  	}
   173  	if headers[0].Number.Uint64() != s.Number+1 {
   174  		return nil, errInvalidVotingChain
   175  	}
   176  	// Iterate through the headers and create a new snapshot
   177  	snap := s.copy()
   178  
   179  	for _, header := range headers {
   180  		// Remove any votes on checkpoint blocks
   181  		number := header.Number.Uint64()
   182  		if number%s.Epoch == 0 {
   183  			snap.Votes = nil
   184  			snap.Tally = make(map[common.Address]Tally)
   185  		}
   186  		// Resolve the authorization key and check against validators
   187  		validator, err := ecrecover(header)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		if _, v := snap.ValSet.GetByAddress(validator); v == nil {
   192  			return nil, errUnauthorized
   193  		}
   194  
   195  		// Header authorized, discard any previous votes from the validator
   196  		for i, vote := range snap.Votes {
   197  			if vote.Validator == validator && vote.Address == header.Coinbase {
   198  				// Uncast the vote from the cached tally
   199  				snap.uncast(vote.Address, vote.Authorize)
   200  
   201  				// Uncast the vote from the chronological list
   202  				snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
   203  				break // only one vote allowed
   204  			}
   205  		}
   206  		// Tally up the new vote from the validator
   207  		var authorize bool
   208  		switch {
   209  		case bytes.Compare(header.Nonce[:], nonceAuthVote) == 0:
   210  			authorize = true
   211  		case bytes.Compare(header.Nonce[:], nonceDropVote) == 0:
   212  			authorize = false
   213  		default:
   214  			return nil, errInvalidVote
   215  		}
   216  		if snap.cast(header.Coinbase, authorize) {
   217  			snap.Votes = append(snap.Votes, &Vote{
   218  				Validator: validator,
   219  				Block:     number,
   220  				Address:   header.Coinbase,
   221  				Authorize: authorize,
   222  			})
   223  		}
   224  		// If the vote passed, update the list of validators
   225  		if tally := snap.Tally[header.Coinbase]; tally.Votes > snap.ValSet.Size()/2 {
   226  			if tally.Authorize {
   227  				snap.ValSet.AddValidator(header.Coinbase)
   228  			} else {
   229  				snap.ValSet.RemoveValidator(header.Coinbase)
   230  
   231  				// Discard any previous votes the deauthorized validator cast
   232  				for i := 0; i < len(snap.Votes); i++ {
   233  					if snap.Votes[i].Validator == header.Coinbase {
   234  						// Uncast the vote from the cached tally
   235  						snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
   236  
   237  						// Uncast the vote from the chronological list
   238  						snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
   239  
   240  						i--
   241  					}
   242  				}
   243  			}
   244  			// Discard any previous votes around the just changed account
   245  			for i := 0; i < len(snap.Votes); i++ {
   246  				if snap.Votes[i].Address == header.Coinbase {
   247  					snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
   248  					i--
   249  				}
   250  			}
   251  			delete(snap.Tally, header.Coinbase)
   252  		}
   253  	}
   254  	snap.Number += uint64(len(headers))
   255  	snap.Hash = headers[len(headers)-1].Hash()
   256  
   257  	return snap, nil
   258  }
   259  
   260  // validators retrieves the list of authorized validators in ascending order.
   261  func (s *Snapshot) validators() []common.Address {
   262  	validators := make([]common.Address, 0, s.ValSet.Size())
   263  	for _, validator := range s.ValSet.List() {
   264  		validators = append(validators, validator.Address())
   265  	}
   266  	for i := 0; i < len(validators); i++ {
   267  		for j := i + 1; j < len(validators); j++ {
   268  			if bytes.Compare(validators[i][:], validators[j][:]) > 0 {
   269  				validators[i], validators[j] = validators[j], validators[i]
   270  			}
   271  		}
   272  	}
   273  	return validators
   274  }