code.vegaprotocol.io/vega@v0.79.0/core/governance/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 governance_test 17 18 import ( 19 "bytes" 20 "context" 21 "testing" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/integration/stubs" 25 "code.vegaprotocol.io/vega/core/netparams" 26 "code.vegaprotocol.io/vega/core/snapshot" 27 "code.vegaprotocol.io/vega/core/stats" 28 "code.vegaprotocol.io/vega/core/types" 29 "code.vegaprotocol.io/vega/libs/proto" 30 vgrand "code.vegaprotocol.io/vega/libs/rand" 31 vgtest "code.vegaprotocol.io/vega/libs/test" 32 "code.vegaprotocol.io/vega/logging" 33 "code.vegaprotocol.io/vega/paths" 34 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 35 36 "github.com/golang/mock/gomock" 37 "github.com/stretchr/testify/assert" 38 "github.com/stretchr/testify/require" 39 ) 40 41 var ( 42 activeKey = (&types.PayloadGovernanceActive{}).Key() 43 enactedKey = (&types.PayloadGovernanceEnacted{}).Key() 44 nodeValidationKey = (&types.PayloadGovernanceNode{}).Key() 45 ) 46 47 func TestGovernanceSnapshotProposalReject(t *testing.T) { 48 eng := getTestEngine(t, time.Now()) 49 50 // get snapshot hash for active proposals 51 emptyState, _, err := eng.GetState(activeKey) 52 require.Nil(t, err) 53 54 // Submit a proposal 55 party := eng.newValidParty("a-valid-party", 123456789) 56 proposal := eng.newProposalForNewMarket(party.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, true) 57 eng.ensureAllAssetEnabled(t) 58 eng.expectOpenProposalEvent(t, party.Id, proposal.ID) 59 60 toSubmit, err := eng.SubmitProposal(context.Background(), *types.ProposalSubmissionFromProposal(&proposal), proposal.ID, party.Id) 61 require.NoError(t, err) 62 63 // get snapshot hash for active proposals 64 s1, _, err := eng.GetState(activeKey) 65 require.Nil(t, err) 66 67 // Reject proposal 68 eng.expectRejectedProposalEvent(t, party.Id, proposal.ID, types.ProposalErrorCouldNotInstantiateMarket) 69 err = eng.RejectProposal(context.Background(), toSubmit.Proposal(), types.ProposalErrorCouldNotInstantiateMarket, assert.AnError) 70 require.NoError(t, err) 71 72 // Check its changed now proposal has been rejected 73 s2, _, err := eng.GetState(activeKey) 74 require.Nil(t, err) 75 require.False(t, bytes.Equal(s1, s2)) 76 77 // Check the hash is the same before we submitted the proposal 78 require.True(t, bytes.Equal(emptyState, s2)) 79 } 80 81 func TestGovernanceSnapshotProposalEnacted(t *testing.T) { 82 eng := getTestEngine(t, time.Now()) 83 84 // get snapshot hashes 85 emptyActive, _, err := eng.GetState(activeKey) 86 require.Nil(t, err) 87 88 emptyEnacted, _, err := eng.GetState(enactedKey) 89 require.Nil(t, err) 90 91 proposer := eng.newValidParty("proposer", 1) 92 voter1 := eng.newValidPartyTimes("voter-1", 7, 2) 93 proposal := eng.newProposalForNewMarket(proposer.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, true) 94 95 eng.ensureStakingAssetTotalSupply(t, 9) 96 eng.ensureAllAssetEnabled(t) 97 eng.expectOpenProposalEvent(t, proposer.Id, proposal.ID) 98 99 // make proposal 100 _, err = eng.submitProposal(t, proposal) 101 102 require.NoError(t, err) 103 104 eng.GetState(activeKey) // we call get state to get change back to false 105 106 // vote for it 107 eng.expectVoteEvent(t, voter1.Id, proposal.ID) 108 err = eng.addYesVote(t, voter1.Id, proposal.ID) 109 require.NoError(t, err) 110 111 eng.GetState(activeKey) // we call get state to get change back to false 112 113 // chain update 114 eng.expectPassedProposalEvent(t, proposal.ID) 115 eng.expectTotalGovernanceTokenFromVoteEvents(t, "1", "7") 116 117 afterClosing := time.Unix(proposal.Terms.ClosingTimestamp, 0).Add(time.Second) 118 eng.expectGetMarketState(t, proposal.ID) 119 eng.OnTick(context.Background(), afterClosing) 120 121 eng.GetState(activeKey) // we call get state to get change back to false 122 123 afterEnactment := time.Unix(proposal.Terms.EnactmentTimestamp, 0).Add(time.Second) 124 eng.OnTick(context.Background(), afterEnactment) 125 126 eng.GetState(activeKey) // we call get state to get change back to false 127 128 // check snapshot hashes (should have no active proposals and one enacted proposal) 129 activeHash, _, err := eng.GetState(activeKey) 130 require.Nil(t, err) 131 require.True(t, bytes.Equal(emptyActive, activeHash)) // active proposal should be gone now its enacted 132 133 enactedHash, _, err := eng.GetState(enactedKey) 134 require.Nil(t, err) 135 require.False(t, bytes.Equal(emptyEnacted, enactedHash)) 136 } 137 138 func TestGovernanceSnapshotWithInternalTimeTerminationProposalEnacted(t *testing.T) { 139 eng := getTestEngine(t, time.Now()) 140 141 // get snapshot hashes 142 emptyActive, _, err := eng.GetState(activeKey) 143 require.Nil(t, err) 144 145 emptyEnacted, _, err := eng.GetState(enactedKey) 146 require.Nil(t, err) 147 148 proposer := eng.newValidParty("proposer", 1) 149 voter1 := eng.newValidPartyTimes("voter-1", 7, 2) 150 proposal := eng.newProposalForNewMarket(proposer.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, false) 151 152 eng.ensureStakingAssetTotalSupply(t, 9) 153 eng.ensureAllAssetEnabled(t) 154 eng.expectOpenProposalEvent(t, proposer.Id, proposal.ID) 155 156 // make proposal 157 _, err = eng.submitProposal(t, proposal) 158 159 require.NoError(t, err) 160 161 eng.GetState(activeKey) // we call get state to get change back to false 162 163 // vote for it 164 eng.expectVoteEvent(t, voter1.Id, proposal.ID) 165 err = eng.addYesVote(t, voter1.Id, proposal.ID) 166 require.NoError(t, err) 167 168 eng.GetState(activeKey) // we call get state to get change back to false 169 170 // chain update 171 eng.expectPassedProposalEvent(t, proposal.ID) 172 eng.expectTotalGovernanceTokenFromVoteEvents(t, "1", "7") 173 174 afterClosing := time.Unix(proposal.Terms.ClosingTimestamp, 0).Add(time.Second) 175 eng.expectGetMarketState(t, proposal.ID) 176 eng.OnTick(context.Background(), afterClosing) 177 178 eng.GetState(activeKey) // we call get state to get change back to false 179 180 afterEnactment := time.Unix(proposal.Terms.EnactmentTimestamp, 0).Add(time.Second) 181 eng.OnTick(context.Background(), afterEnactment) 182 183 eng.GetState(activeKey) // we call get state to get change back to false 184 185 // check snapshot hashes (should have no active proposals and one enacted proposal) 186 activeHash, _, err := eng.GetState(activeKey) 187 require.Nil(t, err) 188 require.True(t, bytes.Equal(emptyActive, activeHash)) // active proposal should be gone now its enacted 189 190 enactedHash, _, err := eng.GetState(enactedKey) 191 require.Nil(t, err) 192 require.False(t, bytes.Equal(emptyEnacted, enactedHash)) 193 } 194 195 func TestGovernanceSnapshotNodeProposal(t *testing.T) { 196 eng := getTestEngine(t, time.Now()) 197 defer eng.ctrl.Finish() 198 199 // get snapshot state for active proposals 200 emptyState, _, err := eng.GetState(nodeValidationKey) 201 require.Nil(t, err) 202 203 // Submit a proposal 204 party := eng.newValidParty("a-valid-party", 123456789) 205 proposal := eng.newProposalForNewAsset(party.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour)) 206 207 eng.expectProposalWaitingForNodeVoteEvent(t, party.Id, proposal.ID) 208 eng.assets.EXPECT().NewAsset(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) 209 eng.assets.EXPECT().Get(gomock.Any()).AnyTimes() 210 eng.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) 211 212 // submit new asset proposal 213 _, err = eng.SubmitProposal(context.Background(), *types.ProposalSubmissionFromProposal(&proposal), proposal.ID, party.Id) 214 require.Nil(t, err) 215 216 // vote on it even though its in waiting-for-node-vote-state 217 voter1 := vgrand.RandomStr(5) 218 eng.ensureTokenBalanceForParty(t, voter1, 1) 219 eng.expectVoteEvent(t, voter1, proposal.ID) 220 err = eng.addYesVote(t, voter1, proposal.ID) 221 require.NoError(t, err) 222 223 // get snapshot state for node proposals and hope its changed 224 s1, _, err := eng.GetState(nodeValidationKey) 225 require.Nil(t, err) 226 require.False(t, bytes.Equal(emptyState, s1)) 227 228 // Get snapshot payload 229 state, _, err := eng.GetState(nodeValidationKey) 230 require.Nil(t, err) 231 232 snap := &snapshotpb.Payload{} 233 err = proto.Unmarshal(state, snap) 234 require.Nil(t, err) 235 236 snapEng := getTestEngine(t, time.Now()) 237 defer snapEng.ctrl.Finish() 238 239 snapEng.assets.EXPECT().NewAsset(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) 240 snapEng.witness.EXPECT().RestoreResource(gomock.Any(), gomock.Any()).Times(1) 241 242 // Load snapshot into a new engine 243 snapEng.broker.EXPECT().Send(gomock.Any()).Times(1) 244 _, err = snapEng.LoadState( 245 context.Background(), 246 types.PayloadFromProto(snap), 247 ) 248 require.Nil(t, err) 249 250 s2, _, err := snapEng.GetState(nodeValidationKey) 251 require.Nil(t, err) 252 require.True(t, bytes.Equal(s1, s2)) 253 254 // check the vote still exists 255 err = proto.Unmarshal(s2, snap) 256 require.Nil(t, err) 257 pp := types.PayloadFromProto(snap) 258 dd := pp.Data.(*types.PayloadGovernanceNode) 259 assert.Equal(t, 1, len(dd.GovernanceNode.ProposalData[0].Yes)) 260 } 261 262 func TestGovernanceSnapshotRoundTrip(t *testing.T) { 263 activeKey := (&types.PayloadGovernanceActive{}).Key() 264 eng := getTestEngine(t, time.Now()) 265 defer eng.ctrl.Finish() 266 267 // initial state 268 emptyState, _, err := eng.GetState(activeKey) 269 require.Nil(t, err) 270 271 proposer := eng.newValidParty("proposer", 1) 272 proposal := eng.newProposalForNewMarket(proposer.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, true) 273 ctx := context.Background() 274 275 eng.ensureAllAssetEnabled(t) 276 eng.expectOpenProposalEvent(t, proposer.Id, proposal.ID) 277 278 _, err = eng.SubmitProposal(ctx, *types.ProposalSubmissionFromProposal(&proposal), proposal.ID, proposer.Id) 279 assert.Nil(t, err) 280 281 s1, _, err := eng.GetState(activeKey) 282 require.Nil(t, err) 283 assert.False(t, bytes.Equal(emptyState, s1)) 284 285 // given 286 voter1 := vgrand.RandomStr(5) 287 eng.ensureTokenBalanceForParty(t, voter1, 1) 288 eng.expectVoteEvent(t, voter1, proposal.ID) 289 err = eng.addYesVote(t, voter1, proposal.ID) 290 require.NoError(t, err) 291 s2, _, err := eng.GetState(activeKey) 292 require.Nil(t, err) 293 assert.False(t, bytes.Equal(s1, s2)) 294 295 snapEng := getTestEngine(t, time.Now()) 296 defer snapEng.ctrl.Finish() 297 298 state, _, err := eng.GetState(activeKey) 299 require.Nil(t, err) 300 301 snap := &snapshotpb.Payload{} 302 err = proto.Unmarshal(state, snap) 303 require.Nil(t, err) 304 305 snapEng.broker.EXPECT().SendBatch(gomock.Any()).Times(2) 306 _, err = snapEng.LoadState(ctx, types.PayloadFromProto(snap)) 307 require.Nil(t, err) 308 309 s3, _, err := snapEng.GetState(activeKey) 310 require.Nil(t, err) 311 require.True(t, bytes.Equal(s2, s3)) 312 } 313 314 func TestGovernanceWithInternalTimeTerminationSnapshotRoundTrip(t *testing.T) { 315 activeKey := (&types.PayloadGovernanceActive{}).Key() 316 eng := getTestEngine(t, time.Now()) 317 defer eng.ctrl.Finish() 318 319 // initial state 320 emptyState, _, err := eng.GetState(activeKey) 321 require.Nil(t, err) 322 323 proposer := eng.newValidParty("proposer", 1) 324 proposal := eng.newProposalForNewMarket(proposer.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, false) 325 ctx := context.Background() 326 327 eng.ensureAllAssetEnabled(t) 328 eng.expectOpenProposalEvent(t, proposer.Id, proposal.ID) 329 330 _, err = eng.SubmitProposal(ctx, *types.ProposalSubmissionFromProposal(&proposal), proposal.ID, proposer.Id) 331 assert.Nil(t, err) 332 333 s1, _, err := eng.GetState(activeKey) 334 require.Nil(t, err) 335 assert.False(t, bytes.Equal(emptyState, s1)) 336 337 // given 338 voter1 := vgrand.RandomStr(5) 339 eng.ensureTokenBalanceForParty(t, voter1, 1) 340 eng.expectVoteEvent(t, voter1, proposal.ID) 341 err = eng.addYesVote(t, voter1, proposal.ID) 342 require.NoError(t, err) 343 s2, _, err := eng.GetState(activeKey) 344 require.Nil(t, err) 345 assert.False(t, bytes.Equal(s1, s2)) 346 347 snapEng := getTestEngine(t, time.Now()) 348 defer snapEng.ctrl.Finish() 349 350 state, _, err := eng.GetState(activeKey) 351 require.Nil(t, err) 352 353 snap := &snapshotpb.Payload{} 354 err = proto.Unmarshal(state, snap) 355 require.Nil(t, err) 356 357 snapEng.broker.EXPECT().SendBatch(gomock.Any()).Times(2) 358 _, err = snapEng.LoadState(ctx, types.PayloadFromProto(snap)) 359 require.Nil(t, err) 360 361 s3, _, err := snapEng.GetState(activeKey) 362 require.Nil(t, err) 363 require.True(t, bytes.Equal(s2, s3)) 364 } 365 366 func TestGovernanceSnapshotEmpty(t *testing.T) { 367 activeKey := (&types.PayloadGovernanceActive{}).Key() 368 eng := getTestEngine(t, time.Now()) 369 defer eng.ctrl.Finish() 370 371 s, _, err := eng.GetState(activeKey) 372 require.Nil(t, err) 373 require.NotNil(t, s) 374 375 s, _, err = eng.GetState(enactedKey) 376 require.Nil(t, err) 377 require.NotNil(t, s) 378 379 s, _, err = eng.GetState(nodeValidationKey) 380 require.Nil(t, err) 381 require.NotNil(t, s) 382 } 383 384 func TestGovernanceSnapshotBatchProposals(t *testing.T) { 385 ctx := vgtest.VegaContext("chainid", 100) 386 387 log := logging.NewTestLogger() 388 389 vegaPath := paths.New(t.TempDir()) 390 391 now := time.Now() 392 timeService := stubs.NewTimeStub() 393 timeService.SetTime(now) 394 395 statsData := stats.New(log, stats.NewDefaultConfig()) 396 397 // Create the engines 398 govEngine1 := getTestEngine(t, now) 399 400 snapshotEngine1, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain) 401 require.NoError(t, err) 402 403 closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close) 404 defer closeSnapshotEngine1() 405 406 snapshotEngine1.AddProviders(govEngine1) 407 408 require.NoError(t, snapshotEngine1.Start(ctx)) 409 410 batchProposalID1 := "batch-1" 411 party := govEngine1.newValidParty("a-valid-party", 123456789) 412 govEngine1.ensureAllAssetEnabled(t) 413 414 govEngine1.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 415 govEngine1.expectOpenProposalEvent(t, party.Id, batchProposalID1) 416 417 proposalNow := govEngine1.tsvc.GetTimeNow().Add(2 * time.Hour) 418 419 sub1 := govEngine1.newBatchSubmission( 420 proposalNow.Add(48*time.Hour).Unix(), 421 govEngine1.newProposalForNewMarket(party.Id, proposalNow, nil, nil, true), 422 govEngine1.newProposalForNetParam(party.Id, netparams.MarketAuctionMaximumDuration, "10h", now), 423 ) 424 _, err = govEngine1.SubmitBatchProposal(ctx, sub1, batchProposalID1, party.Id) 425 require.NoError(t, err) 426 427 batchProposalID2 := "batch-2" 428 govEngine1.expectOpenProposalEvent(t, party.Id, batchProposalID2) 429 430 sub2 := govEngine1.newBatchSubmission( 431 proposalNow.Add(49*time.Hour).Unix(), 432 govEngine1.newProposalForNewMarket(party.Id, now, nil, nil, true), 433 govEngine1.newProposalForNetParam(party.Id, netparams.MarketAuctionMaximumDuration, "10h", now), 434 ) 435 _, err = govEngine1.SubmitBatchProposal(ctx, sub2, batchProposalID2, party.Id) 436 require.NoError(t, err) 437 438 hash1, err := snapshotEngine1.SnapshotNow(ctx) 439 require.NoError(t, err) 440 441 batchProposalID3 := "batch-3" 442 govEngine1.expectOpenProposalEvent(t, party.Id, batchProposalID3) 443 444 sub3 := govEngine1.newBatchSubmission( 445 proposalNow.Add(50*time.Hour).Unix(), 446 govEngine1.newProposalForNewMarket(party.Id, now, nil, nil, true), 447 govEngine1.newProposalForNetParam(party.Id, netparams.MarketAuctionMaximumDuration, "10h", now), 448 ) 449 _, err = govEngine1.SubmitBatchProposal(ctx, sub3, batchProposalID3, party.Id) 450 require.NoError(t, err) 451 452 state1 := map[string][]byte{} 453 for _, key := range govEngine1.Keys() { 454 state, additionalProvider, err := govEngine1.GetState(key) 455 require.NoError(t, err) 456 assert.Empty(t, additionalProvider) 457 state1[key] = state 458 } 459 460 closeSnapshotEngine1() 461 462 // Create the engines 463 govEngine2 := getTestEngine(t, now) 464 snapshotEngine2, err := snapshot.NewEngine(vegaPath, snapshot.DefaultConfig(), log, timeService, statsData.Blockchain) 465 require.NoError(t, err) 466 defer snapshotEngine2.Close() 467 468 snapshotEngine2.AddProviders(govEngine2.Engine) 469 470 govEngine2.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 471 472 require.NoError(t, snapshotEngine2.Start(ctx)) 473 474 hash2, _, _ := snapshotEngine2.Info() 475 require.Equal(t, hash1, hash2) 476 477 party2 := govEngine2.newValidParty("a-valid-party", 123456789) 478 govEngine2.ensureAllAssetEnabled(t) 479 480 govEngine2.expectOpenProposalEvent(t, party2.Id, batchProposalID3) 481 _, err = govEngine2.SubmitBatchProposal(ctx, sub3, batchProposalID3, party2.Id) 482 require.NoError(t, err) 483 484 state2 := map[string][]byte{} 485 for _, key := range govEngine2.Keys() { 486 state, additionalProvider, err := govEngine2.GetState(key) 487 require.NoError(t, err) 488 assert.Empty(t, additionalProvider) 489 state2[key] = state 490 } 491 492 for key := range state1 { 493 assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key) 494 } 495 }