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 }