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 }