code.vegaprotocol.io/vega@v0.79.0/core/banking/snapshot_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 banking_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/hex"
    22  	"strconv"
    23  	"testing"
    24  	"time"
    25  
    26  	"code.vegaprotocol.io/vega/core/assets"
    27  	"code.vegaprotocol.io/vega/core/assets/builtin"
    28  	"code.vegaprotocol.io/vega/core/integration/stubs"
    29  	"code.vegaprotocol.io/vega/core/snapshot"
    30  	snp "code.vegaprotocol.io/vega/core/snapshot"
    31  	"code.vegaprotocol.io/vega/core/stats"
    32  	"code.vegaprotocol.io/vega/core/types"
    33  	"code.vegaprotocol.io/vega/libs/crypto"
    34  	"code.vegaprotocol.io/vega/libs/num"
    35  	"code.vegaprotocol.io/vega/libs/proto"
    36  	vgtest "code.vegaprotocol.io/vega/libs/test"
    37  	"code.vegaprotocol.io/vega/logging"
    38  	"code.vegaprotocol.io/vega/paths"
    39  	"code.vegaprotocol.io/vega/protos/vega"
    40  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    41  
    42  	"github.com/golang/mock/gomock"
    43  	"github.com/stretchr/testify/assert"
    44  	"github.com/stretchr/testify/require"
    45  )
    46  
    47  func deposit(eng *testEngine, asset, party string, amount *num.Uint) *types.BuiltinAssetDeposit {
    48  	eng.OnTick(context.Background(), time.Now())
    49  	return depositAt(eng, asset, party, amount)
    50  }
    51  
    52  func depositAt(eng *testEngine, asset, party string, amount *num.Uint) *types.BuiltinAssetDeposit {
    53  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
    54  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    55  	eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
    56  	return &types.BuiltinAssetDeposit{
    57  		VegaAssetID: asset,
    58  		PartyID:     party,
    59  		Amount:      amount,
    60  	}
    61  }
    62  
    63  func testEngineAndSnapshot(t *testing.T, vegaPath paths.Paths, now time.Time) (*testEngine, *snp.Engine) {
    64  	t.Helper()
    65  	eng := getTestEngine(t)
    66  	log := logging.NewTestLogger()
    67  	timeService := stubs.NewTimeStub()
    68  	timeService.SetTime(now)
    69  	statsData := stats.New(log, stats.NewDefaultConfig())
    70  	config := snp.DefaultConfig()
    71  	snapshotEngine, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain)
    72  	require.NoError(t, err)
    73  	snapshotEngine.AddProviders(eng.Engine)
    74  	return eng, snapshotEngine
    75  }
    76  
    77  func TestSnapshotRoundTripViaEngine(t *testing.T) {
    78  	ctx := vgtest.VegaContext("chainid", 100)
    79  
    80  	now := time.Now()
    81  	deliver := now.Add(time.Hour)
    82  
    83  	testAsset := assets.NewAsset(builtin.New("VGT", &types.AssetDetails{
    84  		Name:   "VEGA TOKEN",
    85  		Symbol: "VGT",
    86  	}))
    87  
    88  	fromAcc := types.Account{
    89  		Balance: num.NewUint(1000),
    90  	}
    91  
    92  	oneoff := &types.TransferFunds{
    93  		Kind: types.TransferCommandKindOneOff,
    94  		OneOff: &types.OneOffTransfer{
    95  			TransferBase: &types.TransferBase{
    96  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
    97  				FromAccountType: types.AccountTypeGeneral,
    98  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
    99  				ToAccountType:   types.AccountTypeGeneral,
   100  				Asset:           "eth",
   101  				Amount:          num.NewUint(10),
   102  				Reference:       "someref",
   103  			},
   104  			DeliverOn: &deliver,
   105  		},
   106  	}
   107  
   108  	oneoff2 := &types.TransferFunds{
   109  		Kind: types.TransferCommandKindOneOff,
   110  		OneOff: &types.OneOffTransfer{
   111  			TransferBase: &types.TransferBase{
   112  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
   113  				FromAccountType: types.AccountTypeGeneral,
   114  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
   115  				ToAccountType:   types.AccountTypeGeneral,
   116  				Asset:           "eth",
   117  				Amount:          num.NewUint(10),
   118  				Reference:       "someref",
   119  			},
   120  			DeliverOn: &deliver,
   121  		},
   122  	}
   123  
   124  	recurring := &types.TransferFunds{
   125  		Kind: types.TransferCommandKindRecurring,
   126  		Recurring: &types.RecurringTransfer{
   127  			TransferBase: &types.TransferBase{
   128  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
   129  				FromAccountType: types.AccountTypeGeneral,
   130  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
   131  				ToAccountType:   types.AccountTypeGeneral,
   132  				Asset:           "eth",
   133  				Amount:          num.NewUint(10),
   134  				Reference:       "someref",
   135  			},
   136  			StartEpoch: 10,
   137  			EndEpoch:   nil, // forever
   138  			Factor:     num.MustDecimalFromString("0.9"),
   139  		},
   140  	}
   141  
   142  	recurring2 := &types.TransferFunds{
   143  		Kind: types.TransferCommandKindRecurring,
   144  		Recurring: &types.RecurringTransfer{
   145  			TransferBase: &types.TransferBase{
   146  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
   147  				FromAccountType: types.AccountTypeGeneral,
   148  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccee",
   149  				ToAccountType:   types.AccountTypeGeneral,
   150  				Asset:           "vega",
   151  				Amount:          num.NewUint(10),
   152  				Reference:       "someref",
   153  			},
   154  			StartEpoch: 10,
   155  			EndEpoch:   nil, // forever
   156  			Factor:     num.MustDecimalFromString("0.9"),
   157  		},
   158  	}
   159  
   160  	d1 := &types.BuiltinAssetDeposit{
   161  		VegaAssetID: "VGT1",
   162  		PartyID:     "someparty1",
   163  		Amount:      num.NewUint(42),
   164  	}
   165  	d2 := &types.BuiltinAssetDeposit{
   166  		VegaAssetID: "VGT1",
   167  		PartyID:     "someparty2",
   168  		Amount:      num.NewUint(24),
   169  	}
   170  	d3 := &types.BuiltinAssetDeposit{
   171  		VegaAssetID: "VGT1",
   172  		PartyID:     "someparty3",
   173  		Amount:      num.NewUint(29),
   174  	}
   175  
   176  	vegaPath := paths.New(t.TempDir())
   177  
   178  	bankingEngine1, snapshotEngine1 := testEngineAndSnapshot(t, vegaPath, now)
   179  	closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close)
   180  	defer closeSnapshotEngine1()
   181  
   182  	bankingEngine1.tsvc.EXPECT().GetTimeNow().Return(now).AnyTimes()
   183  	bankingEngine1.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   184  	bankingEngine1.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
   185  	bankingEngine1.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   186  	bankingEngine1.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil)
   187  	bankingEngine1.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil)
   188  
   189  	require.NoError(t, snapshotEngine1.Start(context.Background()))
   190  
   191  	require.NoError(t, bankingEngine1.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42))
   192  	require.NoError(t, bankingEngine1.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24))
   193  	require.NoError(t, bankingEngine1.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty1", "VGT1", num.NewUint(2)))
   194  	require.NoError(t, bankingEngine1.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty2", "VGT1", num.NewUint(4)))
   195  
   196  	bankingEngine1.OnTick(ctx, now)
   197  
   198  	require.NoError(t, bankingEngine1.TransferFunds(ctx, oneoff))
   199  	require.NoError(t, bankingEngine1.TransferFunds(ctx, recurring))
   200  
   201  	// Take a snapshot.
   202  	hash1, err := snapshotEngine1.SnapshotNow(ctx)
   203  	require.NoError(t, err)
   204  
   205  	// Additional steps to execute the same way on the next engine to verify it yield the same result.
   206  	additionalSteps := func(bankingEngine *testEngine) {
   207  		bankingEngine.OnTick(ctx, now)
   208  
   209  		require.NoError(t, bankingEngine.DepositBuiltinAsset(context.Background(), d3, "depositid3", 29))
   210  		require.NoError(t, bankingEngine.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty1", "VGT1", num.NewUint(10)))
   211  		require.NoError(t, bankingEngine.WithdrawBuiltinAsset(context.Background(), "VGT1", "someparty2", "VGT1", num.NewUint(5)))
   212  
   213  		bankingEngine.OnTick(ctx, now)
   214  
   215  		require.NoError(t, bankingEngine.TransferFunds(ctx, oneoff2))
   216  		require.NoError(t, bankingEngine.TransferFunds(ctx, recurring2))
   217  	}
   218  
   219  	additionalSteps(bankingEngine1)
   220  
   221  	state1 := map[string][]byte{}
   222  	for _, key := range bankingEngine1.Keys() {
   223  		state, additionalProvider, err := bankingEngine1.GetState(key)
   224  		require.NoError(t, err)
   225  		assert.Empty(t, additionalProvider)
   226  		state1[key] = state
   227  	}
   228  
   229  	closeSnapshotEngine1()
   230  
   231  	// Reload the engine using the previous snapshot.
   232  
   233  	bankingEngine2, snapshotEngine2 := testEngineAndSnapshot(t, vegaPath, now)
   234  	defer snapshotEngine2.Close()
   235  
   236  	bankingEngine2.tsvc.EXPECT().GetTimeNow().Return(now).AnyTimes()
   237  	bankingEngine2.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   238  	bankingEngine2.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
   239  	bankingEngine2.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   240  	bankingEngine2.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil)
   241  	bankingEngine2.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil)
   242  
   243  	// This triggers the state restoration from the local snapshot.
   244  	require.NoError(t, snapshotEngine2.Start(ctx))
   245  
   246  	// Comparing the hash after restoration, to ensure it produces the same result.
   247  	hash2, _, _ := snapshotEngine2.Info()
   248  	require.Equal(t, hash1, hash2)
   249  
   250  	// Executing the same steps post-snapshot as the first engine
   251  	additionalSteps(bankingEngine2)
   252  
   253  	state2 := map[string][]byte{}
   254  	for _, key := range bankingEngine2.Keys() {
   255  		state, additionalProvider, err := bankingEngine2.GetState(key)
   256  		require.NoError(t, err)
   257  		assert.Empty(t, additionalProvider)
   258  		state2[key] = state
   259  	}
   260  
   261  	for key := range state1 {
   262  		if key == "withdrawals" {
   263  			// FIXME The withdrawal count inside the engine is not restored by
   264  			//   the snapshot, which leads to a non-deterministic generation
   265  			//   of the withdrawal `Ref` after restoring the state from a snapshot.
   266  			//   Instead of starting at the count from the previous engine, it
   267  			//   restarts at 0.
   268  			//   It doesn't seem to be a big issue. As a result, we skip the
   269  			//   verification for the moment.
   270  			continue
   271  		}
   272  		assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key)
   273  	}
   274  }
   275  
   276  func TestAssetActionsSnapshotRoundTrip(t *testing.T) {
   277  	aaKey := (&types.PayloadBankingAssetActions{}).Key()
   278  	eng := getTestEngine(t)
   279  
   280  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
   281  	d1 := deposit(eng, "VGT1", "someparty1", num.NewUint(42))
   282  	err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42)
   283  	assert.NoError(t, err)
   284  
   285  	d2 := deposit(eng, "VGT1", "someparty2", num.NewUint(24))
   286  	err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24)
   287  	assert.NoError(t, err)
   288  
   289  	state, _, err := eng.GetState(aaKey)
   290  	require.Nil(t, err)
   291  
   292  	// verify state is consistent in the absence of change
   293  	stateNoChange, _, err := eng.GetState(aaKey)
   294  	require.Nil(t, err)
   295  	require.True(t, bytes.Equal(state, stateNoChange))
   296  
   297  	// reload the state
   298  	var assetActions snapshotpb.Payload
   299  	snap := getTestEngine(t)
   300  	snap.tsvc.EXPECT().GetTimeNow().AnyTimes()
   301  	snap.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
   302  
   303  	proto.Unmarshal(state, &assetActions)
   304  	payload := types.PayloadFromProto(&assetActions)
   305  	_, err = snap.LoadState(context.Background(), payload)
   306  	require.Nil(t, err)
   307  	statePostReload, _, _ := snap.GetState(aaKey)
   308  	require.True(t, bytes.Equal(state, statePostReload))
   309  }
   310  
   311  func TestSeenSnapshotRoundTrip(t *testing.T) {
   312  	seenKey := (&types.PayloadBankingSeen{}).Key()
   313  	eng := getTestEngine(t)
   314  
   315  	state1, _, err := eng.GetState(seenKey)
   316  	require.Nil(t, err)
   317  	eng.col.EXPECT().Deposit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil)
   318  
   319  	d1 := deposit(eng, "VGT1", "someparty1", num.NewUint(42))
   320  	err = eng.DepositBuiltinAsset(context.Background(), d1, "depositid1", 42)
   321  	assert.NoError(t, err)
   322  	eng.witness.f(eng.witness.r, true)
   323  
   324  	d2 := deposit(eng, "VGT2", "someparty2", num.NewUint(24))
   325  	err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid2", 24)
   326  	assert.NoError(t, err)
   327  	eng.witness.f(eng.witness.r, true)
   328  
   329  	eng.OnTick(context.Background(), time.Now())
   330  	state2, _, err := eng.GetState(seenKey)
   331  	require.Nil(t, err)
   332  
   333  	require.False(t, bytes.Equal(state1, state2))
   334  
   335  	// verify state is consistent in the absence of change
   336  	stateNoChange, _, err := eng.GetState(seenKey)
   337  	require.Nil(t, err)
   338  	require.True(t, bytes.Equal(state2, stateNoChange))
   339  
   340  	// reload the state
   341  	var seen snapshotpb.Payload
   342  	snap := getTestEngine(t)
   343  	proto.Unmarshal(state2, &seen)
   344  
   345  	payload := types.PayloadFromProto(&seen)
   346  
   347  	_, err = snap.LoadState(context.Background(), payload)
   348  	require.Nil(t, err)
   349  	statePostReload, _, _ := snap.GetState(seenKey)
   350  	require.True(t, bytes.Equal(state2, statePostReload))
   351  }
   352  
   353  func TestWithdrawalsSnapshotRoundTrip(t *testing.T) {
   354  	testAsset := assets.NewAsset(builtin.New("VGT", &types.AssetDetails{
   355  		Name:   "VEGA TOKEN",
   356  		Symbol: "VGT",
   357  	}))
   358  
   359  	withdrawalsKey := (&types.PayloadBankingWithdrawals{}).Key()
   360  	eng := getTestEngine(t)
   361  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
   362  
   363  	for i := 0; i < 10; i++ {
   364  		d1 := deposit(eng, "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), num.NewUint(42))
   365  		err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid"+strconv.Itoa(i*2), 42)
   366  		assert.NoError(t, err)
   367  
   368  		eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   369  		eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
   370  		eng.col.EXPECT().Withdraw(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.LedgerMovement{}, nil)
   371  		err = eng.WithdrawBuiltinAsset(context.Background(), "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), "VGT"+strconv.Itoa(i*2), num.NewUint(2))
   372  		require.Nil(t, err)
   373  		err = eng.WithdrawBuiltinAsset(context.Background(), "VGT"+strconv.Itoa(i*2+1), "someparty"+strconv.Itoa(i*2), "VGT"+strconv.Itoa(i*2), num.NewUint(10))
   374  		require.Nil(t, err)
   375  		state, _, err := eng.GetState(withdrawalsKey)
   376  		require.Nil(t, err)
   377  
   378  		// verify state is consistent in the absence of change
   379  		stateNoChange, _, err := eng.GetState(withdrawalsKey)
   380  		require.Nil(t, err)
   381  		require.True(t, bytes.Equal(state, stateNoChange))
   382  
   383  		// reload the state
   384  		var withdrawals snapshotpb.Payload
   385  		snap := getTestEngine(t)
   386  		proto.Unmarshal(state, &withdrawals)
   387  
   388  		payload := types.PayloadFromProto(&withdrawals)
   389  
   390  		_, err = snap.LoadState(context.Background(), payload)
   391  		require.Nil(t, err)
   392  		statePostReload, _, _ := snap.GetState(withdrawalsKey)
   393  		require.True(t, bytes.Equal(state, statePostReload))
   394  	}
   395  }
   396  
   397  func TestDepositSnapshotRoundTrip(t *testing.T) {
   398  	depositsKey := (&types.PayloadBankingDeposits{}).Key()
   399  	eng := getTestEngine(t)
   400  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
   401  
   402  	for i := 0; i < 10; i++ {
   403  		d1 := deposit(eng, "VGT"+strconv.Itoa(i*2), "someparty"+strconv.Itoa(i*2), num.NewUint(42))
   404  		err := eng.DepositBuiltinAsset(context.Background(), d1, "depositid"+strconv.Itoa(i*2), 42)
   405  		assert.NoError(t, err)
   406  
   407  		d2 := deposit(eng, "VGT"+strconv.Itoa(i*2+1), "someparty"+strconv.Itoa(i*2+1), num.NewUint(24))
   408  		err = eng.DepositBuiltinAsset(context.Background(), d2, "depositid"+strconv.Itoa(i*2+1), 24)
   409  		assert.NoError(t, err)
   410  
   411  		state, _, err := eng.GetState(depositsKey)
   412  		require.Nil(t, err)
   413  
   414  		// verify state is consistent in the absence of change
   415  		stateNoChange, _, err := eng.GetState(depositsKey)
   416  		require.Nil(t, err)
   417  		require.True(t, bytes.Equal(state, stateNoChange))
   418  
   419  		// reload the state
   420  		var deposits snapshotpb.Payload
   421  		snap := getTestEngine(t)
   422  		proto.Unmarshal(state, &deposits)
   423  		payload := types.PayloadFromProto(&deposits)
   424  		_, err = snap.LoadState(context.Background(), payload)
   425  		require.Nil(t, err)
   426  		statePostReload, _, _ := snap.GetState(depositsKey)
   427  		require.True(t, bytes.Equal(state, statePostReload))
   428  	}
   429  }
   430  
   431  func TestOneOffTransfersSnapshotRoundTrip(t *testing.T) {
   432  	ctx := context.Background()
   433  	key := (&types.PayloadBankingScheduledTransfers{}).Key()
   434  	eng := getTestEngine(t)
   435  
   436  	state, _, err := eng.GetState(key)
   437  	require.Nil(t, err)
   438  
   439  	fromAcc := types.Account{
   440  		Balance: num.NewUint(1000),
   441  	}
   442  
   443  	now := time.Unix(1111, 0)
   444  	deliver := now.Add(time.Hour)
   445  	eng.OnTick(ctx, now)
   446  
   447  	oneoff := &types.TransferFunds{
   448  		Kind: types.TransferCommandKindOneOff,
   449  		OneOff: &types.OneOffTransfer{
   450  			TransferBase: &types.TransferBase{
   451  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
   452  				FromAccountType: types.AccountTypeGeneral,
   453  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
   454  				ToAccountType:   types.AccountTypeGeneral,
   455  				Asset:           assetNameETH,
   456  				Amount:          num.NewUint(10),
   457  				Reference:       "someref",
   458  			},
   459  			DeliverOn: &deliver,
   460  		},
   461  	}
   462  
   463  	eng.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   464  	eng.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   465  	eng.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Times(1).Return(&fromAcc, nil)
   466  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   467  	require.NoError(t, eng.TransferFunds(ctx, oneoff))
   468  
   469  	// test the new transfer prompts a change
   470  	state2, _, err := eng.GetState(key)
   471  	require.Nil(t, err)
   472  	require.False(t, bytes.Equal(state, state2))
   473  
   474  	// reload the state
   475  	var transfers snapshotpb.Payload
   476  	snap := getTestEngine(t)
   477  	proto.Unmarshal(state2, &transfers)
   478  	payload := types.PayloadFromProto(&transfers)
   479  	_, err = snap.LoadState(context.Background(), payload)
   480  	require.Nil(t, err)
   481  	statePostReload, _, _ := snap.GetState(key)
   482  	require.True(t, bytes.Equal(state2, statePostReload))
   483  }
   484  
   485  func TestRecurringTransfersSnapshotRoundTrip(t *testing.T) {
   486  	ctx := context.Background()
   487  	key := (&types.PayloadBankingRecurringTransfers{}).Key()
   488  	eng := getTestEngine(t)
   489  
   490  	fromAcc := types.Account{
   491  		Balance: num.NewUint(1000),
   492  	}
   493  
   494  	eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   495  	eng.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil)
   496  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   497  	eng.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   498  
   499  	state, _, err := eng.GetState(key)
   500  	require.Nil(t, err)
   501  
   502  	recurring := &types.TransferFunds{
   503  		Kind: types.TransferCommandKindRecurring,
   504  		Recurring: &types.RecurringTransfer{
   505  			TransferBase: &types.TransferBase{
   506  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
   507  				FromAccountType: types.AccountTypeGeneral,
   508  				To:              "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
   509  				ToAccountType:   types.AccountTypeGeneral,
   510  				Asset:           "eth",
   511  				Amount:          num.NewUint(10),
   512  				Reference:       "someref",
   513  			},
   514  			StartEpoch: 10,
   515  			EndEpoch:   nil, // forever
   516  			Factor:     num.MustDecimalFromString("0.9"),
   517  			DispatchStrategy: &vega.DispatchStrategy{
   518  				AssetForMetric:       "zohar",
   519  				Metric:               vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   520  				Markets:              []string{"mmm"},
   521  				EntityScope:          vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS,
   522  				IndividualScope:      vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM,
   523  				WindowLength:         1,
   524  				LockPeriod:           1,
   525  				DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK,
   526  			},
   527  		},
   528  	}
   529  
   530  	p, _ := proto.Marshal(recurring.Recurring.DispatchStrategy)
   531  	dsHash := hex.EncodeToString(crypto.Hash(p))
   532  
   533  	eng.marketActivityTracker.EXPECT().MarketTrackedForAsset("mmm", "zohar").Times(1).Return(true)
   534  	require.NoError(t, eng.TransferFunds(ctx, recurring))
   535  
   536  	// test the new transfer prompts a change
   537  	state2, _, err := eng.GetState(key)
   538  	require.Nil(t, err)
   539  	require.False(t, bytes.Equal(state, state2))
   540  
   541  	// reload the state
   542  	var transfers snapshotpb.Payload
   543  	snap := getTestEngine(t)
   544  	proto.Unmarshal(state2, &transfers)
   545  	payload := types.PayloadFromProto(&transfers)
   546  	_, err = snap.LoadState(context.Background(), payload)
   547  	require.Nil(t, err)
   548  	statePostReload, _, _ := snap.GetState(key)
   549  	require.True(t, bytes.Equal(state2, statePostReload))
   550  
   551  	require.NotNil(t, eng.GetDispatchStrategy(dsHash))
   552  	require.NotNil(t, snap.GetDispatchStrategy(dsHash))
   553  	require.Equal(t, eng.GetDispatchStrategy(dsHash), snap.GetDispatchStrategy(dsHash))
   554  }
   555  
   556  func TestRecurringGovTransfersSnapshotRoundTrip(t *testing.T) {
   557  	ctx := context.Background()
   558  	key := (&types.PayloadBankingRecurringGovernanceTransfers{}).Key()
   559  	e := getTestEngine(t)
   560  	require.NoError(t, e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1)))
   561  	e.OnTick(ctx, time.Unix(10, 0))
   562  	e.OnEpoch(ctx, types.Epoch{Seq: 1, StartTime: time.Unix(10, 0), Action: vega.EpochAction_EPOCH_ACTION_START})
   563  
   564  	endEpoch := uint64(2)
   565  	transfer := &types.NewTransferConfiguration{
   566  		SourceType:           types.AccountTypeGlobalReward,
   567  		DestinationType:      types.AccountTypeGeneral,
   568  		Asset:                "eth",
   569  		Source:               "",
   570  		Destination:          "zohar",
   571  		TransferType:         vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   572  		MaxAmount:            num.NewUint(10),
   573  		FractionOfBalance:    num.DecimalFromFloat(0.1),
   574  		Kind:                 types.TransferKindRecurring,
   575  		OneOffTransferConfig: nil,
   576  		RecurringTransferConfig: &vega.RecurringTransfer{
   577  			StartEpoch: 1,
   578  			EndEpoch:   &endEpoch,
   579  			DispatchStrategy: &vega.DispatchStrategy{
   580  				AssetForMetric:       "zohar",
   581  				Metric:               vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   582  				Markets:              []string{"mmm"},
   583  				EntityScope:          vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS,
   584  				IndividualScope:      vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM,
   585  				WindowLength:         1,
   586  				LockPeriod:           1,
   587  				DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK,
   588  			},
   589  		},
   590  	}
   591  
   592  	p, _ := proto.Marshal(transfer.RecurringTransferConfig.DispatchStrategy)
   593  	dsHash := hex.EncodeToString(crypto.Hash(p))
   594  
   595  	e.broker.EXPECT().Send(gomock.Any()).Times(1)
   596  	require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer))
   597  
   598  	// test the new transfer prompts a change
   599  	state, _, err := e.GetState(key)
   600  	require.NoError(t, err)
   601  
   602  	// reload the state
   603  	var transfers snapshotpb.Payload
   604  	snap := getTestEngine(t)
   605  	proto.Unmarshal(state, &transfers)
   606  	payload := types.PayloadFromProto(&transfers)
   607  	_, err = snap.LoadState(context.Background(), payload)
   608  	require.Nil(t, err)
   609  	statePostReload, _, _ := snap.GetState(key)
   610  	require.True(t, bytes.Equal(state, statePostReload))
   611  
   612  	require.NotNil(t, e.GetDispatchStrategy(dsHash))
   613  	require.NotNil(t, snap.GetDispatchStrategy(dsHash))
   614  	require.Equal(t, e.GetDispatchStrategy(dsHash), snap.GetDispatchStrategy(dsHash))
   615  }
   616  
   617  func TestScheduledgGovTransfersSnapshotRoundTrip(t *testing.T) {
   618  	ctx := context.Background()
   619  	key := (&types.PayloadBankingScheduledGovernanceTransfers{}).Key()
   620  	e := getTestEngine(t)
   621  
   622  	// let's do a massive fee, easy to test.
   623  	e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))
   624  	e.OnTick(context.Background(), time.Unix(10, 0))
   625  
   626  	deliverOn := time.Unix(12, 0).UnixNano()
   627  	transfer := &types.NewTransferConfiguration{
   628  		SourceType:              types.AccountTypeGlobalReward,
   629  		DestinationType:         types.AccountTypeGeneral,
   630  		Asset:                   "eth",
   631  		Source:                  "",
   632  		Destination:             "zohar",
   633  		TransferType:            vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   634  		MaxAmount:               num.NewUint(10),
   635  		FractionOfBalance:       num.DecimalFromFloat(0.1),
   636  		Kind:                    types.TransferKindOneOff,
   637  		OneOffTransferConfig:    &vega.OneOffTransfer{DeliverOn: deliverOn},
   638  		RecurringTransferConfig: nil,
   639  	}
   640  
   641  	e.broker.EXPECT().Send(gomock.Any()).Times(1)
   642  	require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer))
   643  
   644  	// test the new transfer prompts a change
   645  	state, _, err := e.GetState(key)
   646  	require.NoError(t, err)
   647  
   648  	// reload the state
   649  	var transfers snapshotpb.Payload
   650  	snap := getTestEngine(t)
   651  	proto.Unmarshal(state, &transfers)
   652  	payload := types.PayloadFromProto(&transfers)
   653  	_, err = snap.LoadState(context.Background(), payload)
   654  	require.Nil(t, err)
   655  	statePostReload, _, _ := snap.GetState(key)
   656  	require.True(t, bytes.Equal(state, statePostReload))
   657  }
   658  
   659  func TestAssetListRoundTrip(t *testing.T) {
   660  	ctx := context.Background()
   661  	key := (&types.PayloadBankingAssetActions{}).Key()
   662  	eng := getTestEngine(t)
   663  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
   664  	eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   665  	require.NoError(t, eng.EnableERC20(ctx, &types.ERC20AssetList{}, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", 1000, 1000, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", "1"))
   666  
   667  	state, _, err := eng.GetState(key)
   668  	require.Nil(t, err)
   669  
   670  	var pp snapshotpb.Payload
   671  	proto.Unmarshal(state, &pp)
   672  	payload := types.PayloadFromProto(&pp)
   673  
   674  	snap := getTestEngine(t)
   675  	snap.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   676  	_, err = snap.LoadState(ctx, payload)
   677  	require.Nil(t, err)
   678  
   679  	state2, _, err := snap.GetState(key)
   680  	require.NoError(t, err)
   681  	require.True(t, bytes.Equal(state, state2))
   682  }
   683  
   684  func getTestEngineForSnapshot(t *testing.T) *testEngine {
   685  	t.Helper()
   686  	e := getTestEngine(t)
   687  
   688  	e.tsvc.EXPECT().GetTimeNow().DoAndReturn(
   689  		func() time.Time {
   690  			return time.Unix(10, 0)
   691  		}).AnyTimes()
   692  	e.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
   693  	e.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   694  
   695  	return e
   696  }
   697  
   698  func TestTransferFeeDiscountsSnapshotRoundTrip(t *testing.T) {
   699  	ctx := vgtest.VegaContext("chainid", 100)
   700  
   701  	log := logging.NewTestLogger()
   702  
   703  	vegaPath := paths.New(t.TempDir())
   704  
   705  	now := time.Now()
   706  	timeService := stubs.NewTimeStub()
   707  	timeService.SetTime(now)
   708  
   709  	statsData := stats.New(log, stats.NewDefaultConfig())
   710  	bankingEngine1 := getTestEngineForSnapshot(t)
   711  
   712  	snapshotEngine1, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain)
   713  	require.NoError(t, err)
   714  
   715  	closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close)
   716  	defer closeSnapshotEngine1()
   717  
   718  	snapshotEngine1.AddProviders(bankingEngine1)
   719  
   720  	// No snapshot yet, does nothing.
   721  	require.NoError(t, snapshotEngine1.Start(ctx))
   722  
   723  	// let's do a massive fee, easy to test.
   724  	bankingEngine1.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))
   725  	bankingEngine1.OnTick(context.Background(), time.Unix(10, 0))
   726  
   727  	assetName := "asset-1"
   728  	assetName2 := "asset-2"
   729  	feeDiscounts := map[string]*num.Uint{"party-1": num.NewUint(5), "party-2": num.NewUint(10), "party-6": num.NewUint(33)}
   730  	feeDiscounts2 := map[string]*num.Uint{"party-1": num.NewUint(7), "party-2": num.NewUint(16), "party-4": num.NewUint(16)}
   731  	feeDiscounts3 := map[string]*num.Uint{"party-6": num.NewUint(2), "party-9": num.NewUint(5), "party-2": num.NewUint(33)}
   732  	feeDiscounts4 := map[string]*num.Uint{"party-1": num.NewUint(4), "party-2": num.NewUint(5), "party-6": num.NewUint(6)}
   733  	bankingEngine1.RegisterTradingFees(ctx, assetName, feeDiscounts)
   734  	bankingEngine1.RegisterTradingFees(ctx, assetName2, feeDiscounts2)
   735  	bankingEngine1.RegisterTradingFees(ctx, assetName2, feeDiscounts3)
   736  
   737  	hash1, err := snapshotEngine1.SnapshotNow(ctx)
   738  	require.NoError(t, err)
   739  
   740  	assetName3 := "asset-3"
   741  	bankingEngine1.RegisterTradingFees(ctx, assetName3, feeDiscounts4)
   742  
   743  	state1 := map[string][]byte{}
   744  	for _, key := range bankingEngine1.Keys() {
   745  		state, additionalProvider, err := bankingEngine1.GetState(key)
   746  		require.NoError(t, err)
   747  		assert.Empty(t, additionalProvider)
   748  		state1[key] = state
   749  	}
   750  
   751  	closeSnapshotEngine1()
   752  
   753  	snapshotEngine2, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain)
   754  	require.NoError(t, err)
   755  	defer snapshotEngine2.Close()
   756  
   757  	bankingEngine2 := getTestEngineForSnapshot(t)
   758  
   759  	snapshotEngine2.AddProviders(bankingEngine2.Engine)
   760  
   761  	require.NoError(t, snapshotEngine2.Start(ctx))
   762  
   763  	hash2, _, _ := snapshotEngine2.Info()
   764  	require.Equal(t, hash1, hash2)
   765  
   766  	bankingEngine2.RegisterTradingFees(ctx, assetName3, feeDiscounts4)
   767  
   768  	state2 := map[string][]byte{}
   769  	for _, key := range bankingEngine2.Keys() {
   770  		state, additionalProvider, err := bankingEngine2.GetState(key)
   771  		require.NoError(t, err)
   772  		assert.Empty(t, additionalProvider)
   773  		state2[key] = state
   774  	}
   775  
   776  	for key := range state1 {
   777  		assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key)
   778  	}
   779  }