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  }