code.vegaprotocol.io/vega@v0.79.0/core/snapshot/engine_test.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 snapshot_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/snapshot"
    24  	"code.vegaprotocol.io/vega/core/snapshot/mocks"
    25  	"code.vegaprotocol.io/vega/core/snapshot/tree"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	typemocks "code.vegaprotocol.io/vega/core/types/mocks"
    28  	vegactx "code.vegaprotocol.io/vega/libs/context"
    29  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    30  	"code.vegaprotocol.io/vega/libs/num"
    31  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    32  	vgtest "code.vegaprotocol.io/vega/libs/test"
    33  	"code.vegaprotocol.io/vega/logging"
    34  	"code.vegaprotocol.io/vega/paths"
    35  	"code.vegaprotocol.io/vega/version"
    36  
    37  	tmtypes "github.com/cometbft/cometbft/abci/types"
    38  	"github.com/golang/mock/gomock"
    39  	"github.com/stretchr/testify/assert"
    40  	"github.com/stretchr/testify/require"
    41  )
    42  
    43  func TestEngine(t *testing.T) {
    44  	t.Run("Restoring state succeeds", testRestoringStateSucceeds)
    45  	t.Run("Restoring state at a specific block height succeeds", testRestoringStateAtSpecificBlockHeightSucceeds)
    46  	t.Run("Taking a snapshot succeeds", TestTakingSnapshotSucceeds)
    47  	t.Run("State providers can live under same namespace but with different keys", testProvidersSameNamespaceDifferentKeys)
    48  }
    49  
    50  // testRestoringStateSucceeds restores a state by simulating state-sync, and save
    51  // the snapshot locally. It then simulates restoring the state from the newly
    52  // saved snapshots.
    53  func testRestoringStateSucceeds(t *testing.T) {
    54  	// The snapshot to be restored via state-sync and then, from local storage.
    55  	testSnapshot := firstSnapshot(t)
    56  
    57  	ctrl := gomock.NewController(t)
    58  
    59  	ctx := context.Background()
    60  	vegaPaths := paths.New(t.TempDir())
    61  	log := logging.NewTestLogger()
    62  
    63  	// Some providers matching the snapshot payloads.
    64  	governanceProvider := newGovernanceProvider(t, ctrl)
    65  	delegationProvider := newDelegationProvider(t, ctrl)
    66  	epochProvider := newEpochProvider(t, ctrl)
    67  	statsService := mocks.NewMockStatsService(ctrl)
    68  	timeService := mocks.NewMockTimeService(ctrl)
    69  
    70  	engine, err := snapshot.NewEngine(vegaPaths, snapshot.DefaultConfig(), log, timeService, statsService)
    71  	require.NoError(t, err)
    72  	closeEngine := vgtest.OnlyOnce(engine.Close)
    73  	defer closeEngine()
    74  
    75  	// Since we are initializing an engine, in a brand new environment, the snapshot
    76  	// databases should be empty.
    77  	hasSnapshot, err := engine.HasSnapshots()
    78  	require.NoError(t, err)
    79  	require.False(t, hasSnapshot, "The engine shouldn't have any snapshot")
    80  	latestSnapshots, err := engine.ListLatestSnapshots()
    81  	require.NoError(t, err)
    82  	require.Empty(t, latestSnapshots, "There shouldn't be any snapshot")
    83  
    84  	// Add the providers.
    85  	engine.AddProviders(governanceProvider)
    86  	engine.AddProviders(delegationProvider)
    87  	engine.AddProviders(epochProvider)
    88  
    89  	// From that point, start simulating state-sync.
    90  
    91  	// Starting the engine.
    92  	require.NoError(t, engine.Start(ctx))
    93  
    94  	// No state should be restored because there is no local snapshot.
    95  	require.False(t, engine.HasRestoredStateAlready(), "No state should have been restored")
    96  	// Therefore, the `Info()` should return empty information.
    97  	snapshotHash, height, chainID := engine.Info()
    98  	require.Zero(t, snapshotHash)
    99  	require.Zero(t, height)
   100  	require.Zero(t, chainID)
   101  
   102  	// Simulating the call to the engine from Tendermint ABCI `OfferSnapshot()`.
   103  	response := engine.ReceiveSnapshot(testSnapshot.snapshot)
   104  	require.Equal(t, tmtypes.ResponseOfferSnapshot{
   105  		Result: tmtypes.ResponseOfferSnapshot_ACCEPT,
   106  	}, response)
   107  
   108  	// When all the chunks are loaded, the state restoration is triggered by
   109  	// converting the chunks to payload, that are then broadcast to the providers.
   110  
   111  	timeService.EXPECT().SetTimeNow(gomock.Any(), time.Unix(0, testSnapshot.appState.Time)).Times(1)
   112  	timeService.EXPECT().SetPrevTime(time.Unix(0, testSnapshot.appState.PrevBlockTime)).Times(1)
   113  	statsService.EXPECT().SetHeight(testSnapshot.appState.Height).Times(1)
   114  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceActive()).Return(nil, nil).Times(1)
   115  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceEnacted()).Return(nil, nil).Times(1)
   116  	delegationProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadDelegationActive()).Return(nil, nil).Times(1)
   117  	epochProvider.EXPECT().LoadState(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
   118  
   119  	// Loading each chunk in the engine. When done,  the state restoration is
   120  	// triggered automatically.
   121  	for idx, rawChunk := range testSnapshot.rawChunks {
   122  		response := engine.ReceiveSnapshotChunk(ctx, rawChunk, vgrand.RandomStr(5))
   123  		require.Equal(t, tmtypes.ResponseApplySnapshotChunk{
   124  			Result: tmtypes.ResponseApplySnapshotChunk_ACCEPT,
   125  		}, response, "The raw chunk with index %d should be accepted", idx)
   126  	}
   127  
   128  	// Since the state has been restored, the snapshot databases should have one
   129  	// snapshot saved.
   130  	hasSnapshot, err = engine.HasSnapshots()
   131  	require.NoError(t, err)
   132  	require.True(t, hasSnapshot, "The engine should have a snapshot")
   133  	latestSnapshots, err = engine.ListLatestSnapshots()
   134  	require.NoError(t, err)
   135  	require.Len(t, latestSnapshots, 1, "There should have 1 snapshot")
   136  	require.True(t, engine.HasRestoredStateAlready(), "The state should be marked as restored")
   137  	// And, the method `Info()` should return information of the current state.
   138  	snapshotHash, height, chainID = engine.Info()
   139  	require.Equal(t, testSnapshot.snapshot.Hash, snapshotHash)
   140  	require.EqualValues(t, testSnapshot.appState.Height, height)
   141  	require.Equal(t, testSnapshot.appState.ChainID, chainID)
   142  
   143  	// Start simulating restoration from the local snapshot that has been creating
   144  	// in the previous steps. This also helps verifying the previous snapshot
   145  	// has correctly been saved locally, as it should after a state-sync.
   146  
   147  	// Closing the previous engine instance, so we can simulate a restart.
   148  	closeEngine()
   149  
   150  	engine, err = snapshot.NewEngine(vegaPaths, snapshot.DefaultConfig(), log, timeService, statsService)
   151  	require.NoError(t, err)
   152  	closeEngine = vgtest.OnlyOnce(engine.Close)
   153  	defer closeEngine()
   154  
   155  	// Add same providers as the previous engine instance.
   156  	engine.AddProviders(governanceProvider)
   157  	engine.AddProviders(delegationProvider)
   158  	engine.AddProviders(epochProvider)
   159  
   160  	// Since we should have reload the local snapshot, we should find the previous
   161  	// state loaded but not restored.
   162  	hasSnapshot, err = engine.HasSnapshots()
   163  	require.NoError(t, err)
   164  	require.True(t, hasSnapshot, "The engine should have a snapshot")
   165  	latestSnapshots, err = engine.ListLatestSnapshots()
   166  	require.NoError(t, err)
   167  	require.Len(t, latestSnapshots, 1, "There should have 1 snapshot")
   168  
   169  	// The state is not restored yet.
   170  	require.False(t, engine.HasRestoredStateAlready(), "The state should not be restored yet")
   171  	snapshotHash, height, chainID = engine.Info()
   172  	require.Zero(t, snapshotHash)
   173  	require.Zero(t, height)
   174  	require.Zero(t, chainID)
   175  
   176  	// Setting up the expectation when the the local snapshot will be started.
   177  
   178  	// State restored on the snapshot engine itself, from the local snapshot.
   179  	timeService.EXPECT().SetTimeNow(gomock.Any(), time.Unix(0, testSnapshot.appState.Time)).Times(1)
   180  	timeService.EXPECT().SetPrevTime(time.Unix(0, testSnapshot.appState.PrevBlockTime)).Times(1)
   181  	statsService.EXPECT().SetHeight(testSnapshot.appState.Height).Times(1)
   182  
   183  	// LoadState() is called once for each key. If there are 2 keys, it's called twice.
   184  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceActive()).Return(nil, nil).Times(1)
   185  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceEnacted()).Return(nil, nil).Times(1)
   186  	delegationProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadDelegationActive()).Return(nil, nil).Times(1)
   187  	epochProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadEpoch()).Return(nil, nil).Times(1)
   188  
   189  	// Starting the engine.
   190  	require.NoError(t, engine.Start(ctx))
   191  
   192  	// Since we have a local snapshot, this time, the engine should have restore
   193  	// the state.
   194  	require.True(t, engine.HasRestoredStateAlready(), "The state should be marked as restored")
   195  	// Therefore, the method `Info()` should return information of the current
   196  	// state.
   197  	snapshotHash, height, chainID = engine.Info()
   198  	require.Equal(t, testSnapshot.snapshot.Hash, snapshotHash)
   199  	require.EqualValues(t, testSnapshot.appState.Height, height)
   200  	require.Equal(t, testSnapshot.appState.ChainID, chainID)
   201  
   202  	// Attempt to load a snapshot via state-sync after the state has been restored
   203  	// from the local storage. This should not be possible.
   204  
   205  	response = engine.ReceiveSnapshot(testSnapshot.snapshot)
   206  	require.Equal(t, tmtypes.ResponseOfferSnapshot{
   207  		Result: tmtypes.ResponseOfferSnapshot_ABORT,
   208  	}, response)
   209  
   210  	responseForChunk := engine.ReceiveSnapshotChunk(ctx, testSnapshot.rawChunks[0], vgrand.RandomStr(5))
   211  	require.Equal(t, tmtypes.ResponseApplySnapshotChunk{
   212  		Result: tmtypes.ResponseApplySnapshotChunk_ABORT,
   213  	}, responseForChunk)
   214  
   215  	// Attempt to start the engine a second time to restore state once again
   216  	// from the local storage. This should not be possible.
   217  
   218  	require.Error(t, engine.Start(ctx))
   219  }
   220  
   221  func testRestoringStateAtSpecificBlockHeightSucceeds(t *testing.T) {
   222  	// The snapshot to be restored via state-sync and then, from local storage.
   223  	testSnapshotV1 := firstSnapshot(t)
   224  	testSnapshotV2 := secondSnapshot(t)
   225  
   226  	ctrl := gomock.NewController(t)
   227  
   228  	ctx := context.Background()
   229  	vegaPaths := paths.New(t.TempDir())
   230  	log := logging.NewTestLogger()
   231  
   232  	// Some providers matching the snapshot payloads.
   233  	governanceProvider := newGovernanceProvider(t, ctrl)
   234  	delegationProvider := newDelegationProvider(t, ctrl)
   235  	epochProvider := newEpochProvider(t, ctrl)
   236  	statsService := mocks.NewMockStatsService(ctrl)
   237  	timeService := mocks.NewMockTimeService(ctrl)
   238  
   239  	config := snapshot.DefaultConfig()
   240  
   241  	// We want to restart from the first snapshot. Proposing a more recent
   242  	// snapshot should be rejected.
   243  	config.StartHeight = int64(testSnapshotV1.appState.Height)
   244  	config.RetryLimit = 5
   245  
   246  	engine, err := snapshot.NewEngine(vegaPaths, config, log, timeService, statsService)
   247  	require.NoError(t, err)
   248  	closeEngine := vgtest.OnlyOnce(engine.Close)
   249  	defer closeEngine()
   250  
   251  	// Add the providers.
   252  	engine.AddProviders(governanceProvider)
   253  	engine.AddProviders(delegationProvider)
   254  	engine.AddProviders(epochProvider)
   255  
   256  	// From that point, start simulating state-sync.
   257  
   258  	// Starting the engine.
   259  	require.NoError(t, engine.Start(ctx))
   260  
   261  	// Simulating the call to the engine from Tendermint ABCI `OfferSnapshot()`.
   262  	// We are expecting the v1, so the v2 should be rejected.
   263  	response := engine.ReceiveSnapshot(testSnapshotV2.snapshot)
   264  	require.Equal(t, tmtypes.ResponseOfferSnapshot{
   265  		Result: tmtypes.ResponseOfferSnapshot_REJECT,
   266  	}, response)
   267  
   268  	// Simulating the call to the engine from Tendermint ABCI `OfferSnapshot()`.
   269  	response = engine.ReceiveSnapshot(testSnapshotV1.snapshot)
   270  	require.Equal(t, tmtypes.ResponseOfferSnapshot{
   271  		Result: tmtypes.ResponseOfferSnapshot_ACCEPT,
   272  	}, response)
   273  
   274  	// Attempting to load snapshot chunks that do not match the accepted snapshot
   275  	// to ensure the engine rejects them.
   276  	nodeSendingWrongChunk := vgrand.RandomStr(5)
   277  	responseForChunk := engine.ReceiveSnapshotChunk(ctx, testSnapshotV2.rawChunks[0], nodeSendingWrongChunk)
   278  	require.Equal(t, tmtypes.ResponseApplySnapshotChunk{
   279  		Result:        tmtypes.ResponseApplySnapshotChunk_RETRY,
   280  		RejectSenders: []string{nodeSendingWrongChunk},
   281  	}, responseForChunk, "This raw chunk and its sender should be rejected")
   282  }
   283  
   284  func TestTakingSnapshotSucceeds(t *testing.T) {
   285  	// The snapshot to be restored via state-sync and then, from local storage.
   286  	testSnapshot := firstSnapshot(t)
   287  
   288  	ctrl := gomock.NewController(t)
   289  
   290  	vegaPaths := paths.New(t.TempDir())
   291  	log := logging.NewTestLogger()
   292  	ctx := vegactx.WithChainID(vegactx.WithTraceID(vegactx.WithBlockHeight(context.Background(),
   293  		testSnapshot.appState.Height), testSnapshot.appState.Block), testSnapshot.appState.ChainID,
   294  	)
   295  
   296  	// Some providers matching the snapshot payloads.
   297  	governanceProvider := newGovernanceProvider(t, ctrl)
   298  	delegationProvider := newDelegationProvider(t, ctrl)
   299  	epochProvider := newEpochProvider(t, ctrl)
   300  	statsService := mocks.NewMockStatsService(ctrl)
   301  	timeService := mocks.NewMockTimeService(ctrl)
   302  
   303  	config := snapshot.DefaultConfig()
   304  	// To test we keep 2 snapshots.
   305  	config.KeepRecent = 2
   306  
   307  	engine, err := snapshot.NewEngine(vegaPaths, config, log, timeService, statsService)
   308  	require.NoError(t, err)
   309  	defer engine.Close()
   310  
   311  	// Add the providers.
   312  	engine.AddProviders(governanceProvider)
   313  	engine.AddProviders(delegationProvider)
   314  	engine.AddProviders(epochProvider)
   315  
   316  	// Starting the engine.
   317  	require.NoError(t, engine.Start(ctx))
   318  
   319  	// Set the snapshot interval to 20 to verify the engine only triggers the
   320  	// snapshot at the right moment.
   321  	require.NoError(t, engine.OnSnapshotIntervalUpdate(ctx, num.NewUint(20)))
   322  
   323  	// Attempt to take the snapshot, 9 times, which should do nothing, as the
   324  	// engine is set to take the snapshot every 20 blocks.
   325  	for i := 0; i < 9; i++ {
   326  		hash, _, err := engine.Snapshot(ctx)
   327  		require.NoError(t, err)
   328  		assert.Empty(t, hash)
   329  	}
   330  
   331  	// Decrease the snapshot interval to 10 to verify the engine only triggers the
   332  	// snapshot at the right moment. Since the left attempts (11) are above the new
   333  	// limit, they are reset to the new interval. So it will take 10 attempts
   334  	// to snapshot, like the new interval.
   335  	require.NoError(t, engine.OnSnapshotIntervalUpdate(ctx, num.NewUint(10)))
   336  
   337  	// Attempt to take the snapshot, 9 times, which should do nothing, as the
   338  	// engine is set to take the snapshot every 10 blocks.
   339  	for i := 0; i < 9; i++ {
   340  		hash, _, err := engine.Snapshot(ctx)
   341  		require.NoError(t, err)
   342  		assert.Empty(t, hash)
   343  	}
   344  
   345  	// According to the previous the configuration, the next call to snapshot
   346  	// would have trigger the snapshot. Increase the snapshot interval to 12, this
   347  	// should also re-peg the left attempt, by adding 2 new attempts.
   348  	require.NoError(t, engine.OnSnapshotIntervalUpdate(ctx, num.NewUint(12)))
   349  
   350  	// Attempt to take the snapshot, twice, which should do nothing, again, as
   351  	// the engine is set to take the snapshot every 12 blocks.
   352  	for i := 0; i < 2; i++ {
   353  		hash, _, err := engine.Snapshot(ctx)
   354  		require.NoError(t, err)
   355  		assert.Empty(t, hash)
   356  	}
   357  
   358  	// Add state to the providers for next snapshot attempt.
   359  	governanceActivePayload := testSnapshot.PayloadGovernanceActive()
   360  	governanceEnactedPayload := testSnapshot.PayloadGovernanceEnacted()
   361  	payloadDelegationActive := testSnapshot.PayloadDelegationActive()
   362  	payloadEpoch := testSnapshot.PayloadEpoch()
   363  	governanceProvider.EXPECT().GetState(governanceEnactedPayload.Key()).Return(serialize(t, governanceEnactedPayload), nil, nil).Times(1)
   364  	governanceProvider.EXPECT().GetState(governanceActivePayload.Key()).Return(serialize(t, governanceActivePayload), nil, nil).Times(1)
   365  	delegationProvider.EXPECT().GetState(payloadDelegationActive.Key()).Return(serialize(t, payloadDelegationActive), nil, nil).Times(1)
   366  	epochProvider.EXPECT().GetState(payloadEpoch.Key()).Return(serialize(t, payloadEpoch), nil, nil).Times(1)
   367  	timeService.EXPECT().GetTimeNow().Return(time.Now()).Times(1)
   368  	timeService.EXPECT().GetTimeLastBatch().Return(time.Now()).Times(1)
   369  
   370  	// This time, the snapshot is triggered.
   371  	hash, done, err := engine.Snapshot(ctx)
   372  	require.NoError(t, err)
   373  	assert.NotEmpty(t, hash)
   374  
   375  	// Wait for the async save to be done.
   376  	<-done
   377  
   378  	// Add state to the providers for next snapshot attempt.
   379  	governanceProvider.EXPECT().GetState(governanceEnactedPayload.Key()).Return(serialize(t, governanceEnactedPayload), nil, nil).Times(1)
   380  	governanceProvider.EXPECT().GetState(governanceActivePayload.Key()).Return(serialize(t, governanceActivePayload), nil, nil).Times(1)
   381  	delegationProvider.EXPECT().GetState(payloadDelegationActive.Key()).Return(serialize(t, payloadDelegationActive), nil, nil).Times(1)
   382  	epochProvider.EXPECT().GetState(payloadEpoch.Key()).Return(serialize(t, payloadEpoch), nil, nil).Times(1)
   383  	timeService.EXPECT().GetTimeNow().Return(time.Now()).Times(1)
   384  	timeService.EXPECT().GetTimeLastBatch().Return(time.Now()).Times(1)
   385  
   386  	// First 11 iterations as the snapshot occurs on th 12th one, as we configured
   387  	// above.
   388  	for i := 0; i < 11; i++ {
   389  		hash, _, err := engine.Snapshot(ctx)
   390  		require.NoError(t, err)
   391  		require.Empty(t, hash)
   392  	}
   393  
   394  	// Take a second snapshot on the 12th iteration.
   395  	hash, _, err = engine.Snapshot(ctx)
   396  	require.NoError(t, err)
   397  	require.NotEmpty(t, hash)
   398  
   399  	// Wait for the async save to be done.
   400  	<-done
   401  
   402  	savedSnapshots, err := engine.ListLatestSnapshots()
   403  	require.NoError(t, err)
   404  	assert.Len(t, savedSnapshots, 2)
   405  }
   406  
   407  func testProvidersSameNamespaceDifferentKeys(t *testing.T) {
   408  	ctrl := gomock.NewController(t)
   409  
   410  	vegaPaths := paths.New(t.TempDir())
   411  	log := logging.NewTestLogger()
   412  
   413  	// Some providers matching the snapshot payloads.
   414  	statsService := mocks.NewMockStatsService(ctrl)
   415  	timeService := mocks.NewMockTimeService(ctrl)
   416  
   417  	engine, err := snapshot.NewEngine(vegaPaths, snapshot.DefaultConfig(), log, timeService, statsService)
   418  	require.NoError(t, err)
   419  	defer engine.Close()
   420  
   421  	namespace := types.DelegationSnapshot
   422  	key1 := vgrand.RandomStr(5)
   423  	key2 := vgrand.RandomStr(5)
   424  	key3 := vgrand.RandomStr(5)
   425  	key4 := vgrand.RandomStr(5)
   426  
   427  	provider1 := typemocks.NewMockStateProvider(ctrl)
   428  	provider1.EXPECT().Namespace().Return(namespace).AnyTimes()
   429  	provider1.EXPECT().Keys().Return([]string{key1, key2}).AnyTimes()
   430  	provider1.EXPECT().Stopped().Return(false).AnyTimes()
   431  
   432  	provider2 := typemocks.NewMockStateProvider(ctrl)
   433  	provider2.EXPECT().Namespace().Return(namespace).AnyTimes()
   434  	provider2.EXPECT().Keys().Return([]string{key3}).AnyTimes()
   435  	provider2.EXPECT().Stopped().Return(false).AnyTimes()
   436  
   437  	require.NotPanics(t, func() {
   438  		engine.AddProviders(provider1, provider2)
   439  	})
   440  
   441  	// This provider reuses a key from provider1.
   442  	provider3 := typemocks.NewMockStateProvider(ctrl)
   443  	provider3.EXPECT().Namespace().Return(namespace).AnyTimes()
   444  	provider3.EXPECT().Keys().Return([]string{key2, key4}).AnyTimes()
   445  	provider3.EXPECT().Stopped().Return(false).AnyTimes()
   446  
   447  	require.Panics(t, func() {
   448  		engine.AddProviders(provider3)
   449  	})
   450  }
   451  
   452  func TestProtocolVersionInAppstatePayload(t *testing.T) {
   453  	ctrl := gomock.NewController(t)
   454  
   455  	vegaPaths := paths.New(t.TempDir())
   456  	log := logging.NewTestLogger()
   457  
   458  	// Some providers matching the snapshot payloads.
   459  	statsService := mocks.NewMockStatsService(ctrl)
   460  	timeService := mocks.NewMockTimeService(ctrl)
   461  	engine, err := snapshot.NewEngine(vegaPaths, snapshot.DefaultConfig(), log, timeService, statsService)
   462  	require.NoError(t, err)
   463  
   464  	ctx := vgcontext.WithBlockHeight(context.Background(), 1000)
   465  	ctx = vgcontext.WithChainID(ctx, "chain-1")
   466  	require.NoError(t, engine.Start(ctx))
   467  
   468  	timeService.EXPECT().GetTimeNow().AnyTimes()
   469  	timeService.EXPECT().GetTimeLastBatch().Return(time.Now()).Times(1)
   470  	_, err = engine.SnapshotNow(ctx)
   471  	engine.Close()
   472  	require.NoError(t, err)
   473  
   474  	snapshotTree, err := tree.New(log, tree.WithLevelDBDatabase(vegaPaths))
   475  	require.NoError(t, err)
   476  	payloads, err := snapshotTree.AsPayloads()
   477  	require.NoError(t, err)
   478  
   479  	var appstate *types.PayloadAppState
   480  	for _, p := range payloads {
   481  		if p.Namespace() == types.AppSnapshot {
   482  			appstate = p.GetAppState()
   483  			break
   484  		}
   485  	}
   486  	require.NotNil(t, appstate)
   487  	assert.True(t, appstate.AppState.ProtocolUpdgade)
   488  	assert.Equal(t, version.Get(), appstate.AppState.ProtocolVersion)
   489  }
   490  
   491  func TestSnapshotVersionCommunicatedToProviders(t *testing.T) {
   492  	// The snapshot to be restored via state-sync and then, from local storage.
   493  	testSnapshot := firstSnapshot(t)
   494  
   495  	ctrl := gomock.NewController(t)
   496  
   497  	vegaPaths := paths.New(t.TempDir())
   498  	log := logging.NewTestLogger()
   499  
   500  	statsService := mocks.NewMockStatsService(ctrl)
   501  	timeService := mocks.NewMockTimeService(ctrl)
   502  	governanceProvider := newGovernanceProvider(t, ctrl)
   503  	delegationProvider := newDelegationProvider(t, ctrl)
   504  	epochProvider := newEpochProvider(t, ctrl)
   505  
   506  	// new engine same vega-home
   507  	engine, err := snapshot.NewEngine(vegaPaths, snapshot.DefaultConfig(), log, timeService, statsService)
   508  	require.NoError(t, err)
   509  	defer engine.Close()
   510  
   511  	engine.AddProviders(governanceProvider, delegationProvider, epochProvider)
   512  
   513  	engine.Start(context.Background())
   514  
   515  	// Simulating the call to the engine from Tendermint ABCI `OfferSnapshot()`.
   516  	response := engine.ReceiveSnapshot(testSnapshot.snapshot)
   517  	require.Equal(t, tmtypes.ResponseOfferSnapshot{
   518  		Result: tmtypes.ResponseOfferSnapshot_ACCEPT,
   519  	}, response)
   520  
   521  	// When all the chunks are loaded, the state restoration is triggered by
   522  	// converting the chunks to payload, that are then broadcast to the providers.
   523  
   524  	timeService.EXPECT().SetTimeNow(gomock.Any(), time.Unix(0, testSnapshot.appState.Time)).Times(1)
   525  	timeService.EXPECT().SetPrevTime(time.Unix(0, testSnapshot.appState.PrevBlockTime)).Times(1)
   526  	statsService.EXPECT().SetHeight(testSnapshot.appState.Height).Times(1)
   527  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceActive()).Return(nil, nil).Times(1)
   528  	governanceProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadGovernanceEnacted()).Return(nil, nil).Times(1)
   529  	delegationProvider.EXPECT().LoadState(gomock.Any(), testSnapshot.PayloadDelegationActive()).Return(nil, nil).Times(1)
   530  
   531  	var isUpgradeFrom bool
   532  	epochProvider.EXPECT().LoadState(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1).Do(
   533  		func(rctx context.Context, _ *types.Payload) {
   534  			isUpgradeFrom = vegactx.InProgressUpgradeFrom(rctx, "v0.72.1")
   535  		},
   536  	)
   537  
   538  	// Loading each chunk in the engine. When done,  the state restoration is
   539  	// triggered automatically.
   540  	for idx, rawChunk := range testSnapshot.rawChunks {
   541  		response := engine.ReceiveSnapshotChunk(context.Background(), rawChunk, vgrand.RandomStr(5))
   542  		require.Equal(t, tmtypes.ResponseApplySnapshotChunk{
   543  			Result: tmtypes.ResponseApplySnapshotChunk_ACCEPT,
   544  		}, response, "The raw chunk with index %d should be accepted", idx)
   545  	}
   546  
   547  	require.True(t, isUpgradeFrom)
   548  }