github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/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  	"encoding/json"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    24  	"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/ethdb"
    27  	"github.com/ethereum/go-ethereum/log"
    28  )
    29  
    30  const (
    31  	dbKeySnapshotPrefix = "istanbul-snapshot"
    32  )
    33  
    34  // Snapshot is the state of the authorization voting at a given point in time.
    35  type Snapshot struct {
    36  	Epoch uint64 // The number of blocks for each epoch
    37  
    38  	Number uint64                // Block number where the snapshot was created
    39  	Hash   common.Hash           // Block hash where the snapshot was created
    40  	ValSet istanbul.ValidatorSet // Set of authorized validators at this moment
    41  }
    42  
    43  // newSnapshot create a new snapshot with the specified startup parameters. This
    44  // method does not initialize the set of recent validators, so only ever use if for
    45  // the genesis block.
    46  func newSnapshot(epoch uint64, number uint64, hash common.Hash, valSet istanbul.ValidatorSet) *Snapshot {
    47  	snap := &Snapshot{
    48  		Epoch:  epoch,
    49  		Number: number,
    50  		Hash:   hash,
    51  		ValSet: valSet,
    52  	}
    53  	return snap
    54  }
    55  
    56  // loadSnapshot loads an existing snapshot from the database.
    57  func loadSnapshot(epoch uint64, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
    58  	blob, err := db.Get(append([]byte(dbKeySnapshotPrefix), hash[:]...))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	snap := new(Snapshot)
    63  	if err := json.Unmarshal(blob, snap); err != nil {
    64  		return nil, err
    65  	}
    66  	snap.Epoch = epoch
    67  
    68  	return snap, nil
    69  }
    70  
    71  // store inserts the snapshot into the database.
    72  func (s *Snapshot) store(db ethdb.Database) error {
    73  	blob, err := json.Marshal(s)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	return db.Put(append([]byte(dbKeySnapshotPrefix), s.Hash[:]...), blob)
    78  }
    79  
    80  // copy creates a deep copy of the snapshot, though not the individual votes.
    81  func (s *Snapshot) copy() *Snapshot {
    82  	cpy := &Snapshot{
    83  		Epoch:  s.Epoch,
    84  		Number: s.Number,
    85  		Hash:   s.Hash,
    86  		ValSet: s.ValSet.Copy(),
    87  	}
    88  
    89  	return cpy
    90  }
    91  
    92  // apply creates a new authorization snapshot by applying the given headers to
    93  // the original one.
    94  func (s *Snapshot) apply(headers []*types.Header, db ethdb.Database) (*Snapshot, error) {
    95  	// Allow passing in no headers for cleaner code
    96  	if len(headers) == 0 {
    97  		return s, nil
    98  	}
    99  
   100  	// Sanity check that the headers can be applied
   101  	for i := 0; i < len(headers)-1; i++ {
   102  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+s.Epoch {
   103  			return nil, errInvalidVotingChain
   104  		}
   105  	}
   106  	if headers[0].Number.Uint64() != s.Number+s.Epoch {
   107  		return nil, errInvalidVotingChain
   108  	}
   109  
   110  	// Iterate through the headers and create a new snapshot
   111  	snap := s.copy()
   112  
   113  	for _, header := range headers {
   114  		// Resolve the authorization key and check against validators
   115  		validator, err := ecrecover(header)
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  		if _, v := snap.ValSet.GetByAddress(validator); v == nil {
   120  			return nil, errUnauthorized
   121  		}
   122  
   123  		// Ensure that the extra data format is satisfied
   124  		istExtra, err := types.ExtractIstanbulExtra(header)
   125  		if err != nil {
   126  			log.Error("Unable to extract the istanbul extra field from the header", "header", header)
   127  			return nil, err
   128  		}
   129  
   130  		validators, err := istanbul.CombineIstanbulExtraToValidatorData(istExtra.AddedValidators, istExtra.AddedValidatorsPublicKeys)
   131  		if err != nil {
   132  			log.Error("Error in combining addresses and public keys")
   133  			return nil, errInvalidValidatorSetDiff
   134  		}
   135  
   136  		if !snap.ValSet.RemoveValidators(istExtra.RemovedValidators) {
   137  			log.Error("Error in removing the header's RemovedValidators")
   138  			return nil, errInvalidValidatorSetDiff
   139  		}
   140  		if !snap.ValSet.AddValidators(validators) {
   141  			log.Error("Error in adding the header's AddedValidators")
   142  			return nil, errInvalidValidatorSetDiff
   143  		}
   144  
   145  		snap.Epoch = s.Epoch
   146  		snap.Number += s.Epoch
   147  		snap.Hash = header.Hash()
   148  		snap.store(db)
   149  		log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
   150  	}
   151  
   152  	return snap, nil
   153  }
   154  
   155  func (s *Snapshot) validators() []istanbul.ValidatorData {
   156  	validators := make([]istanbul.ValidatorData, 0, s.ValSet.Size())
   157  	for _, validator := range s.ValSet.List() {
   158  		validators = append(validators, istanbul.ValidatorData{
   159  			validator.Address(),
   160  			validator.BLSPublicKey(),
   161  		})
   162  	}
   163  	return validators
   164  }
   165  
   166  type snapshotJSON struct {
   167  	Epoch  uint64      `json:"epoch"`
   168  	Number uint64      `json:"number"`
   169  	Hash   common.Hash `json:"hash"`
   170  
   171  	// for validator set
   172  	Validators []istanbul.ValidatorData `json:"validators"`
   173  }
   174  
   175  func (s *Snapshot) toJSONStruct() *snapshotJSON {
   176  	validators := s.validators()
   177  	return &snapshotJSON{
   178  		Epoch:      s.Epoch,
   179  		Number:     s.Number,
   180  		Hash:       s.Hash,
   181  		Validators: validators,
   182  	}
   183  }
   184  
   185  // UnmarshalJSON from a json byte array
   186  func (s *Snapshot) UnmarshalJSON(b []byte) error {
   187  	var j snapshotJSON
   188  	if err := json.Unmarshal(b, &j); err != nil {
   189  		return err
   190  	}
   191  
   192  	s.Epoch = j.Epoch
   193  	s.Number = j.Number
   194  	s.Hash = j.Hash
   195  	s.ValSet = validator.NewSet(j.Validators)
   196  	return nil
   197  }
   198  
   199  // MarshalJSON to a json byte array
   200  func (s *Snapshot) MarshalJSON() ([]byte, error) {
   201  	j := s.toJSONStruct()
   202  	return json.Marshal(j)
   203  }