code.vegaprotocol.io/vega@v0.79.0/core/vesting/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 vesting_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/assets"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  	vgtest "code.vegaprotocol.io/vega/libs/test"
    27  	"code.vegaprotocol.io/vega/paths"
    28  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestSnapshotEngine(t *testing.T) {
    36  	ctx := vgtest.VegaContext("chainid", 100)
    37  
    38  	vegaPath := paths.New(t.TempDir())
    39  	now := time.Now()
    40  
    41  	te1 := newEngine(t)
    42  	snapshotEngine1 := newSnapshotEngine(t, vegaPath, now, te1.engine)
    43  	closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close)
    44  	defer closeSnapshotEngine1()
    45  
    46  	require.NoError(t, snapshotEngine1.Start(ctx))
    47  
    48  	setupMocks(t, te1)
    49  	setupNetParams(ctx, t, te1)
    50  
    51  	te1.engine.AddReward(context.Background(), "party1", "eth", num.NewUint(100), 4)
    52  	te1.engine.AddReward(context.Background(), "party1", "btc", num.NewUint(150), 1)
    53  	te1.engine.AddReward(context.Background(), "party1", "eth", num.NewUint(200), 0)
    54  
    55  	nextEpoch(ctx, t, te1, time.Now())
    56  
    57  	te1.engine.AddReward(context.Background(), "party2", "btc", num.NewUint(100), 2)
    58  	te1.engine.AddReward(context.Background(), "party3", "btc", num.NewUint(100), 0)
    59  
    60  	nextEpoch(ctx, t, te1, time.Now())
    61  
    62  	te1.engine.AddReward(context.Background(), "party4", "eth", num.NewUint(100), 1)
    63  	te1.engine.AddReward(context.Background(), "party5", "doge", num.NewUint(100), 0)
    64  
    65  	// Take a snapshot.
    66  	hash1, err := snapshotEngine1.SnapshotNow(ctx)
    67  	require.NoError(t, err)
    68  	snapshottedEpoch := te1.currentEpoch
    69  
    70  	// This is what must be replayed after snapshot restoration.
    71  	replayFn := func(te *testSnapshotEngine) {
    72  		te.engine.AddReward(context.Background(), "party6", "doge", num.NewUint(100), 3)
    73  
    74  		nextEpoch(ctx, t, te, time.Now())
    75  
    76  		te.engine.AddReward(context.Background(), "party7", "eth", num.NewUint(100), 2)
    77  		te.engine.AddReward(context.Background(), "party8", "vega", num.NewUint(100), 10)
    78  
    79  		nextEpoch(ctx, t, te, time.Now())
    80  	}
    81  
    82  	replayFn(te1)
    83  
    84  	state1 := map[string][]byte{}
    85  	for _, key := range te1.engine.Keys() {
    86  		state, additionalProvider, err := te1.engine.GetState(key)
    87  		require.NoError(t, err)
    88  		assert.Empty(t, additionalProvider)
    89  		state1[key] = state
    90  	}
    91  
    92  	closeSnapshotEngine1()
    93  
    94  	// Reload the engine using the previous snapshot.
    95  
    96  	te2 := newEngine(t)
    97  	snapshotEngine2 := newSnapshotEngine(t, vegaPath, now, te2.engine)
    98  	defer snapshotEngine2.Close()
    99  
   100  	setupMocks(t, te2)
   101  	setupNetParams(ctx, t, te2)
   102  
   103  	// Ensure the engine's epoch (and test helpers) starts at the same epoch the
   104  	// first engine has been snapshotted.
   105  	te2.currentEpoch = snapshottedEpoch
   106  	te2.engine.OnEpochRestore(ctx, types.Epoch{
   107  		Seq:    snapshottedEpoch,
   108  		Action: vegapb.EpochAction_EPOCH_ACTION_START,
   109  	})
   110  
   111  	// This triggers the state restoration from the local snapshot.
   112  	require.NoError(t, snapshotEngine2.Start(ctx))
   113  
   114  	// Comparing the hash after restoration, to ensure it produces the same result.
   115  	hash2, _, _ := snapshotEngine2.Info()
   116  	require.Equal(t, hash1, hash2)
   117  
   118  	// Replaying the same commands after snapshot has been taken with first engine.
   119  	replayFn(te2)
   120  
   121  	state2 := map[string][]byte{}
   122  	for _, key := range te2.engine.Keys() {
   123  		state, additionalProvider, err := te2.engine.GetState(key)
   124  		require.NoError(t, err)
   125  		assert.Empty(t, additionalProvider)
   126  		state2[key] = state
   127  	}
   128  
   129  	for key := range state1 {
   130  		assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key)
   131  	}
   132  }
   133  
   134  func setupNetParams(ctx context.Context, t *testing.T, te *testSnapshotEngine) {
   135  	t.Helper()
   136  
   137  	require.NoError(t, te.engine.OnBenefitTiersUpdate(ctx, &vegapb.VestingBenefitTiers{
   138  		Tiers: []*vegapb.VestingBenefitTier{
   139  			{
   140  				MinimumQuantumBalance: "10000",
   141  				RewardMultiplier:      "1.5",
   142  			},
   143  			{
   144  				MinimumQuantumBalance: "100000",
   145  				RewardMultiplier:      "2",
   146  			},
   147  			{
   148  				MinimumQuantumBalance: "500000",
   149  				RewardMultiplier:      "2.5",
   150  			},
   151  		},
   152  	}))
   153  
   154  	require.NoError(t, te.engine.OnRewardVestingBaseRateUpdate(ctx, num.MustDecimalFromString("0.9")))
   155  	require.NoError(t, te.engine.OnRewardVestingMinimumTransferUpdate(ctx, num.MustDecimalFromString("1")))
   156  }
   157  
   158  func setupMocks(t *testing.T, te *testSnapshotEngine) {
   159  	t.Helper()
   160  
   161  	te.asvm.EXPECT().GetRewardsVestingMultiplier(gomock.Any()).AnyTimes().Return(num.MustDecimalFromString("1"))
   162  	te.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(dummyAsset{quantum: 10}), nil)
   163  	te.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   164  	te.parties.EXPECT().RelatedKeys(gomock.Any()).AnyTimes()
   165  }