code.vegaprotocol.io/vega@v0.79.0/core/banking/checkpoint_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  	"testing"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/assets"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/protos/vega"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  const assetNameETH = "ETH"
    35  
    36  func TestCheckpoint(t *testing.T) {
    37  	t.Run("test simple scheduled transfer", testSimpledScheduledTransfer)
    38  }
    39  
    40  func TestDepositFinalisedAfterCheckpoint(t *testing.T) {
    41  	eng := getTestEngine(t)
    42  
    43  	eng.tsvc.EXPECT().GetTimeNow().AnyTimes()
    44  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    45  	eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
    46  	eng.OnTick(context.Background(), time.Now())
    47  	bad := &types.BuiltinAssetDeposit{
    48  		VegaAssetID: "VGT",
    49  		PartyID:     "someparty",
    50  		Amount:      num.NewUint(42),
    51  	}
    52  
    53  	// call the deposit function
    54  	err := eng.DepositBuiltinAsset(context.Background(), bad, "depositid", 42)
    55  	assert.NoError(t, err)
    56  
    57  	// then we call the callback from the fake witness
    58  	eng.witness.r.Check(context.Background())
    59  	eng.witness.f(eng.witness.r, true)
    60  
    61  	// now we take a checkpoint
    62  	cp, err := eng.Checkpoint()
    63  	require.NoError(t, err)
    64  
    65  	loadEng := getTestEngine(t)
    66  
    67  	loadEng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(testAsset, nil)
    68  	loadEng.tsvc.EXPECT().GetTimeNow().AnyTimes()
    69  	loadEng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    70  
    71  	// load from checkpoint
    72  	require.NoError(t, loadEng.Load(context.Background(), cp))
    73  
    74  	// now finalise the asset action
    75  	// then we call time update, which should call the collateral to
    76  	// to do the deposit
    77  	loadEng.col.EXPECT().Deposit(gomock.Any(), bad.PartyID, bad.VegaAssetID, bad.Amount).Times(1).Return(&types.LedgerMovement{}, nil)
    78  	loadEng.OnTick(context.Background(), time.Now())
    79  }
    80  
    81  func testSimpledScheduledTransfer(t *testing.T) {
    82  	e := getTestEngine(t)
    83  
    84  	// let's do a massive fee, easy to test.
    85  	e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))
    86  	e.OnTick(context.Background(), time.Unix(10, 0))
    87  
    88  	deliverOn := time.Unix(12, 0)
    89  	ctx := context.Background()
    90  	transfer := &types.TransferFunds{
    91  		Kind: types.TransferCommandKindOneOff,
    92  		OneOff: &types.OneOffTransfer{
    93  			TransferBase: &types.TransferBase{
    94  				From:            "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
    95  				FromAccountType: types.AccountTypeGeneral,
    96  				To:              "0000000000000000000000000000000000000000000000000000000000000000",
    97  				ToAccountType:   types.AccountTypeGlobalReward,
    98  				Asset:           assetNameETH,
    99  				Amount:          num.NewUint(10),
   100  				Reference:       "someref",
   101  			},
   102  			DeliverOn: &deliverOn,
   103  		},
   104  	}
   105  
   106  	fromAcc := types.Account{
   107  		Balance: num.NewUint(100),
   108  	}
   109  
   110  	// asset exists
   111  	e.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   112  	e.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).Times(1).Return(&fromAcc, nil)
   113  
   114  	// assert the calculation of fees and transfer request are correct
   115  	e.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
   116  		func(ctx context.Context,
   117  			transfers []*types.Transfer,
   118  			accountTypes []types.AccountType,
   119  			references []string,
   120  			feeTransfers []*types.Transfer,
   121  			feeTransfersAccountTypes []types.AccountType,
   122  		) ([]*types.LedgerMovement, error,
   123  		) {
   124  			t.Run("ensure transfers are correct", func(t *testing.T) {
   125  				// transfer is done fully instantly, we should have 2 transfer
   126  				assert.Len(t, transfers, 1)
   127  				assert.Equal(t, transfers[0].Owner, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301")
   128  				assert.Equal(t, transfers[0].Amount.Amount, num.NewUint(10))
   129  				assert.Equal(t, transfers[0].Amount.Asset, assetNameETH)
   130  
   131  				// 2 account types too
   132  				assert.Len(t, accountTypes, 1)
   133  				assert.Equal(t, accountTypes[0], types.AccountTypeGeneral)
   134  			})
   135  
   136  			t.Run("ensure fee transfers are correct", func(t *testing.T) {
   137  				assert.Len(t, feeTransfers, 1)
   138  				assert.Equal(t, feeTransfers[0].Owner, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301")
   139  				assert.Equal(t, feeTransfers[0].Amount.Amount, num.NewUint(10))
   140  				assert.Equal(t, feeTransfers[0].Amount.Asset, assetNameETH)
   141  
   142  				// then the fees account types
   143  				assert.Len(t, feeTransfersAccountTypes, 1)
   144  				assert.Equal(t, accountTypes[0], types.AccountTypeGeneral)
   145  			})
   146  			return nil, nil
   147  		})
   148  
   149  	e.broker.EXPECT().Send(gomock.Any()).Times(3)
   150  	assert.NoError(t, e.TransferFunds(ctx, transfer))
   151  
   152  	checkp, err := e.Checkpoint()
   153  	assert.NoError(t, err)
   154  
   155  	// now second step, we start a new engine, and load the checkpoint
   156  	e2 := getTestEngine(t)
   157  	defer e2.ctrl.Finish()
   158  
   159  	// load the checkpoint
   160  	e2.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   161  	assert.NoError(t, e2.Load(ctx, checkp))
   162  
   163  	// then trigger the time update, and see the transfer
   164  	// assert the calculation of fees and transfer request are correct
   165  	e2.tsvc.EXPECT().GetTimeNow().DoAndReturn(
   166  		func() time.Time {
   167  			return time.Unix(12, 0)
   168  		}).AnyTimes()
   169  
   170  	e2.broker.EXPECT().Send(gomock.Any()).Times(1)
   171  	e2.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
   172  		func(ctx context.Context,
   173  			transfers []*types.Transfer,
   174  			accountTypes []types.AccountType,
   175  			references []string,
   176  			feeTransfers []*types.Transfer,
   177  			feeTransfersAccountTypes []types.AccountType,
   178  		) ([]*types.LedgerMovement, error,
   179  		) {
   180  			t.Run("ensure transfers are correct", func(t *testing.T) {
   181  				// transfer is done fully instantly, we should have 2 transfer
   182  				assert.Equal(t, transfers[0].Owner, "0000000000000000000000000000000000000000000000000000000000000000")
   183  				assert.Equal(t, transfers[0].Amount.Amount, num.NewUint(10))
   184  				assert.Equal(t, transfers[0].Amount.Asset, assetNameETH)
   185  
   186  				// 1 account types too
   187  				assert.Len(t, accountTypes, 1)
   188  				assert.Equal(t, accountTypes[0], types.AccountTypeGlobalReward)
   189  			})
   190  
   191  			t.Run("ensure fee transfers are correct", func(t *testing.T) {
   192  				assert.Len(t, feeTransfers, 0)
   193  			})
   194  			return nil, nil
   195  		})
   196  
   197  	e2.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   198  	e2.OnTick(context.Background(), time.Unix(12, 0))
   199  }
   200  
   201  func TestGovernanceScheduledTransfer(t *testing.T) {
   202  	e := getTestEngine(t)
   203  	e.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   204  
   205  	// let's do a massive fee, easy to test.
   206  	e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))
   207  	e.OnTick(context.Background(), time.Unix(10, 0))
   208  
   209  	deliverOn := time.Unix(12, 0).UnixNano()
   210  
   211  	ctx := context.Background()
   212  	transfer := &types.NewTransferConfiguration{
   213  		SourceType:              types.AccountTypeGlobalReward,
   214  		DestinationType:         types.AccountTypeGeneral,
   215  		Asset:                   assetNameETH,
   216  		Source:                  "",
   217  		Destination:             "zohar",
   218  		TransferType:            vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   219  		MaxAmount:               num.NewUint(10),
   220  		FractionOfBalance:       num.DecimalFromFloat(0.1),
   221  		Kind:                    types.TransferKindOneOff,
   222  		OneOffTransferConfig:    &vega.OneOffTransfer{DeliverOn: deliverOn},
   223  		RecurringTransferConfig: nil,
   224  	}
   225  
   226  	e.broker.EXPECT().Send(gomock.Any()).Times(1)
   227  	require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer))
   228  
   229  	checkp, err := e.Checkpoint()
   230  	assert.NoError(t, err)
   231  
   232  	// now second step, we start a new engine, and load the checkpoint
   233  	e2 := getTestEngine(t)
   234  	defer e2.ctrl.Finish()
   235  	e2.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   236  
   237  	// load the checkpoint
   238  	e2.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   239  	require.NoError(t, e2.Load(ctx, checkp))
   240  
   241  	chp2, err := e2.Checkpoint()
   242  	require.NoError(t, err)
   243  	require.True(t, bytes.Equal(chp2, checkp))
   244  
   245  	// progress time to when the scheduled gov transfer should be delivered on
   246  	// then trigger the time update, and see the transfer going
   247  	e2.broker.EXPECT().Send(gomock.Any()).Times(2)
   248  	e2.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   249  	e2.col.EXPECT().GetSystemAccountBalance(gomock.Any(), gomock.Any(), gomock.Any()).Return(num.NewUint(1000), nil).AnyTimes()
   250  	e2.OnMaxAmountChanged(context.Background(), num.DecimalFromInt64(100000))
   251  	e2.OnMaxFractionChanged(context.Background(), num.DecimalFromFloat(0.5))
   252  	e2.col.EXPECT().GovernanceTransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
   253  	e2.OnTick(context.Background(), time.Unix(12, 0))
   254  
   255  	// expect the transfer to have been removed from the engine so the checkpoint has changed
   256  	chp3, err := e2.Checkpoint()
   257  	require.NoError(t, err)
   258  	require.False(t, bytes.Equal(chp2, chp3))
   259  }
   260  
   261  func TestGovernanceRecurringTransfer(t *testing.T) {
   262  	e := getTestEngine(t)
   263  	e.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil)
   264  
   265  	ctx := context.Background()
   266  	e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1))
   267  	e.OnTick(ctx, time.Unix(10, 0))
   268  	e.OnEpoch(ctx, types.Epoch{Seq: 0, StartTime: time.Unix(10, 0), Action: vega.EpochAction_EPOCH_ACTION_START})
   269  
   270  	endEpoch := uint64(2)
   271  
   272  	transfer := &types.NewTransferConfiguration{
   273  		SourceType:              types.AccountTypeGlobalReward,
   274  		DestinationType:         types.AccountTypeGeneral,
   275  		Asset:                   assetNameETH,
   276  		Source:                  "",
   277  		Destination:             "zohar",
   278  		TransferType:            vega.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   279  		MaxAmount:               num.NewUint(10),
   280  		FractionOfBalance:       num.DecimalFromFloat(0.1),
   281  		Kind:                    types.TransferKindRecurring,
   282  		OneOffTransferConfig:    nil,
   283  		RecurringTransferConfig: &vega.RecurringTransfer{StartEpoch: 1, EndEpoch: &endEpoch, Factor: "1"},
   284  	}
   285  
   286  	e.broker.EXPECT().Send(gomock.Any()).Times(1)
   287  	require.NoError(t, e.NewGovernanceTransfer(ctx, "1", "some reference", transfer))
   288  
   289  	checkp, err := e.Checkpoint()
   290  	require.NoError(t, err)
   291  
   292  	// now second step, we start a new engine, and load the checkpoint
   293  	e2 := getTestEngine(t)
   294  	defer e2.ctrl.Finish()
   295  	e2.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil).AnyTimes()
   296  
   297  	// load the checkpoint
   298  	e2.broker.EXPECT().SendBatch(gomock.Any()).Times(2)
   299  	require.NoError(t, e2.Load(ctx, checkp))
   300  
   301  	chp2, err := e2.Checkpoint()
   302  	require.NoError(t, err)
   303  	require.True(t, bytes.Equal(chp2, checkp))
   304  
   305  	// now lets end epoch 0 and 1 so that we can get the transfer out
   306  	e2.col.EXPECT().GetSystemAccountBalance(gomock.Any(), gomock.Any(), gomock.Any()).Return(num.NewUint(1000), nil).AnyTimes()
   307  	e2.OnMaxAmountChanged(context.Background(), num.DecimalFromInt64(10000))
   308  	e2.OnMaxFractionChanged(context.Background(), num.DecimalFromFloat(0.5))
   309  	e2.col.EXPECT().GovernanceTransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
   310  	e2.OnEpoch(ctx, types.Epoch{Seq: 0, StartTime: time.Unix(10, 0), Action: vega.EpochAction_EPOCH_ACTION_END})
   311  	e2.OnEpoch(ctx, types.Epoch{Seq: 1, StartTime: time.Unix(20, 0), Action: vega.EpochAction_EPOCH_ACTION_START})
   312  	e2.broker.EXPECT().Send(gomock.Any()).Times(2)
   313  	e2.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   314  	e2.OnEpoch(ctx, types.Epoch{Seq: 1, StartTime: time.Unix(20, 0), Action: vega.EpochAction_EPOCH_ACTION_END})
   315  
   316  	// now end epoch 2 and expect the second transfer to be delivered and the transfer to be terminated
   317  	e2.col.EXPECT().GovernanceTransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
   318  	e2.OnEpoch(ctx, types.Epoch{Seq: 2, StartTime: time.Unix(30, 0), Action: vega.EpochAction_EPOCH_ACTION_START})
   319  	e2.OnEpoch(ctx, types.Epoch{Seq: 2, StartTime: time.Unix(30, 0), Action: vega.EpochAction_EPOCH_ACTION_END})
   320  
   321  	chp3, err := e2.Checkpoint()
   322  	require.NoError(t, err)
   323  	require.False(t, bytes.Equal(chp2, chp3))
   324  }