code.vegaprotocol.io/vega@v0.79.0/core/validators/topology_snapshot.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package validators 17 18 import ( 19 "context" 20 "encoding/base64" 21 "math/rand" 22 "sort" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/events" 26 "code.vegaprotocol.io/vega/core/types" 27 vegactx "code.vegaprotocol.io/vega/libs/context" 28 "code.vegaprotocol.io/vega/libs/num" 29 "code.vegaprotocol.io/vega/libs/proto" 30 "code.vegaprotocol.io/vega/logging" 31 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 32 snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 33 34 tmtypes "github.com/cometbft/cometbft/abci/types" 35 ) 36 37 var ( 38 topKey = (&types.PayloadTopology{}).Key() 39 40 topHashKeys = []string{ 41 topKey, 42 } 43 ) 44 45 type topologySnapshotState struct { 46 serialised []byte 47 } 48 49 func (t *Topology) Namespace() types.SnapshotNamespace { 50 return types.TopologySnapshot 51 } 52 53 func (t *Topology) Keys() []string { 54 return topHashKeys 55 } 56 57 func (t *Topology) Stopped() bool { 58 return false 59 } 60 61 func (t *Topology) serialiseNodes() []*snapshot.ValidatorState { 62 nodes := make([]*snapshot.ValidatorState, 0, len(t.validators)) 63 for _, node := range t.validators { 64 nodes = append(nodes, 65 &snapshot.ValidatorState{ 66 ValidatorUpdate: &eventspb.ValidatorUpdate{ 67 NodeId: node.data.ID, 68 VegaPubKey: node.data.VegaPubKey, 69 VegaPubKeyIndex: node.data.VegaPubKeyIndex, 70 EthereumAddress: node.data.EthereumAddress, 71 TmPubKey: node.data.TmPubKey, 72 InfoUrl: node.data.InfoURL, 73 Country: node.data.Country, 74 Name: node.data.Name, 75 AvatarUrl: node.data.AvatarURL, 76 FromEpoch: node.data.FromEpoch, 77 SubmitterAddress: node.data.SubmitterAddress, 78 }, 79 BlockAdded: uint64(node.blockAdded), 80 Status: int32(node.status), 81 StatusChangeBlock: uint64(node.statusChangeBlock), 82 LastBlockWithPositiveRanking: uint64(node.lastBlockWithPositiveRanking), 83 EthEventsForwarded: node.numberOfEthereumEventsForwarded, 84 HeartbeatTracker: &snapshot.HeartbeatTracker{ 85 BlockSigs: node.heartbeatTracker.blockSigs[:], 86 BlockIndex: int32(node.heartbeatTracker.blockIndex), 87 ExpectedNextHash: node.heartbeatTracker.expectedNextHash, 88 ExpectedNextHashSince: node.heartbeatTracker.expectedNexthashSince.UnixNano(), 89 }, 90 ValidatorPower: node.validatorPower, 91 RankingScore: node.rankingScore, 92 }, 93 ) 94 } 95 96 sort.SliceStable(nodes, func(i, j int) bool { return nodes[i].ValidatorUpdate.NodeId < nodes[j].ValidatorUpdate.NodeId }) 97 return nodes 98 } 99 100 func (t *Topology) serialisePendingKeyRotation() []*snapshot.PendingKeyRotation { 101 // len(t.pendingPubKeyRotations)*2 - assuming there is at least one rotation per blockHeight 102 pkrs := make([]*snapshot.PendingKeyRotation, 0, len(t.pendingPubKeyRotations)*2) 103 104 for blockHeight, rotations := range t.pendingPubKeyRotations { 105 for nodeID, pr := range rotations { 106 pkrs = append(pkrs, &snapshot.PendingKeyRotation{ 107 BlockHeight: blockHeight, 108 NodeId: nodeID, 109 NewPubKey: pr.newPubKey, 110 NewPubKeyIndex: pr.newKeyIndex, 111 }) 112 } 113 } 114 115 sort.SliceStable(pkrs, func(i, j int) bool { 116 if pkrs[i].GetBlockHeight() == pkrs[j].GetBlockHeight() { 117 return pkrs[i].GetNodeId() < pkrs[j].GetNodeId() 118 } 119 return pkrs[i].GetBlockHeight() < pkrs[j].GetBlockHeight() 120 }) 121 122 return pkrs 123 } 124 125 func (t *Topology) serialisePendingEthereumKeyRotation() []*snapshot.PendingEthereumKeyRotation { 126 // len(t.pendingEthKeyRotations)*2 - assuming there is at least one rotation per blockHeight 127 pkrs := make([]*snapshot.PendingEthereumKeyRotation, 0, len(t.pendingEthKeyRotations)*2) 128 129 for blockHeight, rotations := range t.pendingEthKeyRotations { 130 for _, r := range rotations { 131 pkrs = append(pkrs, &snapshot.PendingEthereumKeyRotation{ 132 BlockHeight: blockHeight, 133 NodeId: r.NodeID, 134 NewAddress: r.NewAddress, 135 Submitter: r.SubmitterAddress, 136 OldAddress: r.OldAddress, 137 }) 138 } 139 } 140 141 sort.SliceStable(pkrs, func(i, j int) bool { 142 if pkrs[i].GetBlockHeight() == pkrs[j].GetBlockHeight() { 143 return pkrs[i].GetNodeId() < pkrs[j].GetNodeId() 144 } 145 return pkrs[i].GetBlockHeight() < pkrs[j].GetBlockHeight() 146 }) 147 148 return pkrs 149 } 150 151 func (t *Topology) serialiseUnresolvedEthereumKeyRotations() []*snapshot.PendingEthereumKeyRotation { 152 ukrs := make([]*snapshot.PendingEthereumKeyRotation, 0, len(t.unresolvedEthKeyRotations)) 153 for _, r := range t.unresolvedEthKeyRotations { 154 ukrs = append(ukrs, &snapshot.PendingEthereumKeyRotation{ 155 NodeId: r.NodeID, 156 NewAddress: r.NewAddress, 157 Submitter: r.SubmitterAddress, 158 OldAddress: r.OldAddress, 159 }) 160 } 161 162 sort.SliceStable(ukrs, func(i, j int) bool { return ukrs[i].GetNodeId() < ukrs[j].GetNodeId() }) 163 164 return ukrs 165 } 166 167 // serialise gets the serialised form of the given key. 168 func (t *Topology) serialise(k string) ([]byte, error) { 169 if k != topKey { 170 return nil, ErrSnapshotKeyDoesNotExist 171 } 172 173 t.mu.Lock() 174 defer t.mu.Unlock() 175 176 payload := types.Payload{ 177 Data: &types.PayloadTopology{ 178 Topology: &types.Topology{ 179 ChainValidators: t.chainValidators[:], 180 ValidatorData: t.serialiseNodes(), 181 PendingPubKeyRotations: t.serialisePendingKeyRotation(), 182 PendingEthereumKeyRotations: t.serialisePendingEthereumKeyRotation(), 183 UnresolvedEthereumKeyRotations: t.serialiseUnresolvedEthereumKeyRotations(), 184 Signatures: t.signatures.SerialisePendingSignatures(), 185 ValidatorPerformance: t.validatorPerformance.Serialize(), 186 }, 187 }, 188 } 189 data, err := proto.Marshal(payload.IntoProto()) 190 if err != nil { 191 return nil, err 192 } 193 194 t.tss.serialised = data 195 return data, nil 196 } 197 198 func (t *Topology) GetState(k string) ([]byte, []types.StateProvider, error) { 199 state, err := t.serialise(k) 200 return state, nil, err 201 } 202 203 func (t *Topology) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) { 204 if t.Namespace() != p.Data.Namespace() { 205 return nil, types.ErrInvalidSnapshotNamespace 206 } 207 // see what we're reloading 208 switch pl := p.Data.(type) { 209 case *types.PayloadTopology: 210 return nil, t.restore(ctx, pl.Topology, p) 211 default: 212 return nil, types.ErrUnknownSnapshotType 213 } 214 } 215 216 func (t *Topology) restorePendingKeyRotations(pkrs []*snapshot.PendingKeyRotation) { 217 for _, pkr := range pkrs { 218 if _, ok := t.pendingPubKeyRotations[pkr.BlockHeight]; !ok { 219 t.pendingPubKeyRotations[pkr.BlockHeight] = map[string]pendingKeyRotation{} 220 } 221 222 t.pendingPubKeyRotations[pkr.BlockHeight][pkr.NodeId] = pendingKeyRotation{ 223 newPubKey: pkr.NewPubKey, 224 newKeyIndex: pkr.NewPubKeyIndex, 225 } 226 } 227 } 228 229 func (t *Topology) restorePendingEthereumKeyRotations(pkrs []*snapshot.PendingEthereumKeyRotation) { 230 for _, pkr := range pkrs { 231 t.pendingEthKeyRotations.add(pkr.BlockHeight, 232 PendingEthereumKeyRotation{ 233 NodeID: pkr.NodeId, 234 NewAddress: pkr.NewAddress, 235 OldAddress: pkr.OldAddress, 236 SubmitterAddress: pkr.Submitter, 237 }) 238 } 239 } 240 241 func (t *Topology) restoreUnresolvedEthereumKeyRotations(ukrs []*snapshot.PendingEthereumKeyRotation) { 242 for _, u := range ukrs { 243 t.unresolvedEthKeyRotations[u.NodeId] = PendingEthereumKeyRotation{ 244 NodeID: u.NodeId, 245 NewAddress: u.NewAddress, 246 OldAddress: u.OldAddress, 247 SubmitterAddress: u.Submitter, 248 } 249 } 250 } 251 252 func (t *Topology) restore(ctx context.Context, topology *types.Topology, p *types.Payload) error { 253 t.mu.Lock() 254 defer t.mu.Unlock() 255 t.log.Debug("restoring topology snapshot") 256 t.validators = map[string]*valState{} 257 258 vUpdates := []tmtypes.ValidatorUpdate{} 259 260 epochSeq := num.NewUint(t.epochSeq).String() 261 for _, node := range topology.ValidatorData { 262 t.log.Debug("restoring validator data snapshot", logging.String("nodeid", node.ValidatorUpdate.NodeId)) 263 vs := &valState{ 264 data: ValidatorData{ 265 ID: node.ValidatorUpdate.NodeId, 266 VegaPubKey: node.ValidatorUpdate.VegaPubKey, 267 VegaPubKeyIndex: node.ValidatorUpdate.VegaPubKeyIndex, 268 EthereumAddress: node.ValidatorUpdate.EthereumAddress, 269 TmPubKey: node.ValidatorUpdate.TmPubKey, 270 InfoURL: node.ValidatorUpdate.InfoUrl, 271 Country: node.ValidatorUpdate.Country, 272 Name: node.ValidatorUpdate.Name, 273 AvatarURL: node.ValidatorUpdate.AvatarUrl, 274 FromEpoch: node.ValidatorUpdate.FromEpoch, 275 SubmitterAddress: node.ValidatorUpdate.SubmitterAddress, 276 }, 277 blockAdded: int64(node.BlockAdded), 278 status: ValidatorStatus(node.Status), 279 statusChangeBlock: int64(node.StatusChangeBlock), 280 lastBlockWithPositiveRanking: int64(node.LastBlockWithPositiveRanking), 281 numberOfEthereumEventsForwarded: node.EthEventsForwarded, 282 heartbeatTracker: &validatorHeartbeatTracker{ 283 blockIndex: int(node.HeartbeatTracker.BlockIndex), 284 expectedNextHash: node.HeartbeatTracker.ExpectedNextHash, 285 expectedNexthashSince: time.Unix(0, node.HeartbeatTracker.ExpectedNextHashSince), 286 }, 287 validatorPower: node.ValidatorPower, 288 rankingScore: node.RankingScore, 289 } 290 for i := 0; i < 10; i++ { 291 vs.heartbeatTracker.blockSigs[i] = node.HeartbeatTracker.BlockSigs[i] 292 } 293 t.validators[node.ValidatorUpdate.NodeId] = vs 294 295 t.sendValidatorUpdateEvent(ctx, vs.data, true) 296 297 // send an event with the current ranking of the validator 298 if node.RankingScore != nil { 299 t.broker.Send(events.NewValidatorRanking(ctx, epochSeq, node.ValidatorUpdate.NodeId, node.RankingScore.StakeScore, node.RankingScore.PerformanceScore, node.RankingScore.RankingScore, protoStatusToString(node.RankingScore.PreviousStatus), protoStatusToString(node.RankingScore.Status), int(node.RankingScore.VotingPower))) 300 } 301 302 // this node is started and expect to be a validator 303 // but so far we haven't seen ourselves as validators for 304 // this network. 305 if t.isValidatorSetup && !t.isValidator { 306 t.checkValidatorDataWithSelfWallets(vs.data) 307 } 308 309 if node.Status == ValidatorStatusTendermint { 310 pubkey, err := base64.StdEncoding.DecodeString(node.ValidatorUpdate.TmPubKey) 311 if err != nil { 312 t.log.Panic("failed to decode tendermint public key", logging.String("tm-pub-key", node.ValidatorUpdate.TmPubKey)) 313 } 314 vUpdates = append(vUpdates, tmtypes.UpdateValidator(pubkey, node.ValidatorPower, "")) 315 } 316 } 317 318 bh, err := vegactx.BlockHeightFromContext(ctx) 319 if err != nil { 320 t.log.Panic("failed to restore current block-height from context", logging.Error(err)) 321 } 322 323 t.currentBlockHeight = bh 324 t.validatorPowerUpdates = vUpdates 325 t.chainValidators = topology.ChainValidators[:] 326 t.restorePendingKeyRotations(topology.PendingPubKeyRotations) 327 t.restorePendingEthereumKeyRotations(topology.PendingEthereumKeyRotations) 328 t.restoreUnresolvedEthereumKeyRotations(topology.UnresolvedEthereumKeyRotations) 329 330 t.signatures.RestorePendingSignatures(topology.Signatures) 331 t.signatures.SetNonce(t.timeService.GetTimeNow()) 332 333 t.validatorPerformance.Deserialize(topology.ValidatorPerformance) 334 t.tss.serialised, err = proto.Marshal(p.IntoProto()) 335 t.rng = rand.New(rand.NewSource(t.timeService.GetTimeNow().Unix())) 336 return err 337 } 338 339 // OnEpochRestore is the epochtime service telling us the restored epoch data. 340 func (t *Topology) OnEpochRestore(_ context.Context, epoch types.Epoch) { 341 t.log.Debug("epoch restoration notification received", logging.String("epoch", epoch.String())) 342 t.epochSeq = epoch.Seq 343 // we always take at snapshot at commit-time after the end of a block, so newEpochStarted will always be false when we restore because either 344 // 1) we aren't at the start of an epoch and so newEpochStarted is obviously false 345 // 2) we are at the start of an epoch, but at the end of the block *before* we take the snapshot we reset the powers and set newEpochStarted to false 346 t.newEpochStarted = false 347 }