code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/stake_linking_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 sqlstore_test 17 18 import ( 19 "context" 20 "fmt" 21 "testing" 22 "time" 23 24 "code.vegaprotocol.io/vega/datanode/entities" 25 "code.vegaprotocol.io/vega/datanode/sqlstore" 26 "code.vegaprotocol.io/vega/libs/num" 27 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 28 29 "github.com/georgysavva/scany/pgxscan" 30 "github.com/shopspring/decimal" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestStakeLinkingStore(t *testing.T) { 36 t.Run("Upsert should add a stake linking record if it doesn't exist in the current block", testUpsertShouldAddNewInBlock) 37 t.Run("Upsert should update a stake linking record if it already exists in the current block", testUpsertShouldUpdateExistingInBlock) 38 t.Run("GetStake should return the most current version of each stake linking record and calculate the total stake available", testGetStake) 39 } 40 41 func setupStakeLinkingTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.StakeLinking) { 42 t.Helper() 43 bs := sqlstore.NewBlocks(connectionSource) 44 sl := sqlstore.NewStakeLinking(connectionSource) 45 return bs, sl 46 } 47 48 func testUpsertShouldAddNewInBlock(t *testing.T) { 49 ctx := tempTransaction(t) 50 51 bs, sl := setupStakeLinkingTest(t) 52 53 var rowCount int 54 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 55 assert.Equal(t, 0, rowCount) 56 57 block := addTestBlock(t, ctx, bs) 58 stakingProtos := getStakingProtos() 59 60 proto := stakingProtos[0] 61 data, err := entities.StakeLinkingFromProto(proto, generateTxHash(), block.VegaTime) 62 require.NoError(t, err) 63 assert.NoError(t, sl.Upsert(ctx, data)) 64 65 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 66 assert.Equal(t, 1, rowCount) 67 } 68 69 func testUpsertShouldUpdateExistingInBlock(t *testing.T) { 70 ctx := tempTransaction(t) 71 72 bs, sl := setupStakeLinkingTest(t) 73 var rowCount int 74 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 75 assert.Equal(t, 0, rowCount) 76 77 block := addTestBlock(t, ctx, bs) 78 stakingProtos := getStakingProtos() 79 80 for _, proto := range stakingProtos { 81 data, err := entities.StakeLinkingFromProto(proto, generateTxHash(), block.VegaTime) 82 require.NoError(t, err) 83 assert.NoError(t, sl.Upsert(ctx, data)) 84 } 85 86 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 87 assert.Equal(t, 2, rowCount) 88 } 89 90 func testGetStake(t *testing.T) { 91 ctx := tempTransaction(t) 92 93 bs, sl := setupStakeLinkingTest(t) 94 95 var rowCount int 96 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 97 assert.Equal(t, 0, rowCount) 98 99 block := addTestBlock(t, ctx, bs) 100 stakingProtos := getStakingProtos() 101 102 for _, proto := range stakingProtos { 103 data, err := entities.StakeLinkingFromProto(proto, generateTxHash(), block.VegaTime) 104 require.NoError(t, err) 105 assert.NoError(t, sl.Upsert(ctx, data)) 106 } 107 108 assert.NoError(t, connectionSource.QueryRow(ctx, "select count(*) from stake_linking").Scan(&rowCount)) 109 assert.Equal(t, 2, rowCount) 110 111 partyID := entities.PartyID("cafed00d") 112 113 currentBalance, links, _, err := sl.GetStake(ctx, partyID, entities.CursorPagination{}) 114 require.NoError(t, err) 115 want := num.NewUint(30002) 116 assert.True(t, want.EQ(currentBalance)) 117 assert.Equal(t, 2, len(links)) 118 } 119 120 func getStakingProtos() []*eventspb.StakeLinking { 121 return []*eventspb.StakeLinking{ 122 { 123 Id: "deadbeef", 124 Type: eventspb.StakeLinking_TYPE_LINK, 125 Ts: time.Now().Unix(), 126 Party: "cafed00d", 127 Amount: "10000", 128 Status: eventspb.StakeLinking_STATUS_ACCEPTED, 129 FinalizedAt: time.Now().UnixNano(), 130 TxHash: "0xfe179560b9d0cc44c5fea54c2167c1cee7ccfcabf294752a4f43fb64ddffda85", 131 BlockHeight: 1000000, 132 BlockTime: 0, 133 LogIndex: 100000, 134 EthereumAddress: "TEST", 135 }, 136 { 137 Id: "deadbeef", 138 Type: eventspb.StakeLinking_TYPE_LINK, 139 Ts: time.Now().Unix(), 140 Party: "cafed00d", 141 Amount: "10001", 142 Status: eventspb.StakeLinking_STATUS_ACCEPTED, 143 FinalizedAt: time.Now().UnixNano(), 144 TxHash: "0xfe179560b9d0cc44c5fea54c2167c1cee7ccfcabf294752a4f43fb64ddffda85", 145 BlockHeight: 1000000, 146 BlockTime: 0, 147 LogIndex: 100000, 148 EthereumAddress: "TEST", 149 }, 150 { 151 Id: "deadbaad", 152 Type: eventspb.StakeLinking_TYPE_LINK, 153 Ts: time.Now().Unix(), 154 Party: "cafed00d", 155 Amount: "20001", 156 Status: eventspb.StakeLinking_STATUS_ACCEPTED, 157 FinalizedAt: time.Now().UnixNano(), 158 TxHash: "0xfe179560b9d0cc44c5fea54c2167c1cee7ccfcabf294752a4f43fb64ddffda85", 159 BlockHeight: 1000000, 160 BlockTime: 0, 161 LogIndex: 100000, 162 EthereumAddress: "TEST", 163 }, 164 } 165 } 166 167 func TestStakeLinkingPagination(t *testing.T) { 168 t.Run("should return all stake linkings if no pagination is specified", testStakeLinkingPaginationNoPagination) 169 t.Run("should return first page of stake linkings if first is provided", testStakeLinkingPaginationFirst) 170 t.Run("should return last page of stake linkings if last is provided", testStakeLinkingPaginationLast) 171 t.Run("should return specified page of stake linkings if first and after is specified", testStakeLinkingPaginationFirstAndAfter) 172 t.Run("should return specified page of stake linkings if last and before is specified", testStakeLinkingPaginationLastAndBefore) 173 174 t.Run("should return all stake linkings if no pagination is specified - newest first", testStakeLinkingPaginationNoPaginationNewestFirst) 175 t.Run("should return first page of stake linkings if first is provided - newest first", testStakeLinkingPaginationFirstNewestFirst) 176 t.Run("should return last page of stake linkings if last is provided - newest first", testStakeLinkingPaginationLastNewestFirst) 177 t.Run("should return specified page of stake linkings if first and after is specified - newest first", testStakeLinkingPaginationFirstAndAfterNewestFirst) 178 t.Run("should return specified page of stake linkings if last and before is specified - newest first", testStakeLinkingPaginationLastAndBeforeNewestFirst) 179 } 180 181 func testStakeLinkingPaginationNoPagination(t *testing.T) { 182 ctx := tempTransaction(t) 183 184 ls, links := setupStakeLinkingPaginationTest(t, ctx) 185 186 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false) 187 require.NoError(t, err) 188 partyID := entities.PartyID("cafed00d") 189 190 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 191 require.NoError(t, err) 192 want := links[10:] 193 assert.Equal(t, want, got) 194 assert.Equal(t, entities.PageInfo{ 195 HasNextPage: false, 196 HasPreviousPage: false, 197 StartCursor: want[0].Cursor().Encode(), 198 EndCursor: want[9].Cursor().Encode(), 199 }, pageInfo) 200 } 201 202 func testStakeLinkingPaginationFirst(t *testing.T) { 203 ctx := tempTransaction(t) 204 205 ls, links := setupStakeLinkingPaginationTest(t, ctx) 206 207 first := int32(3) 208 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false) 209 require.NoError(t, err) 210 partyID := entities.PartyID("cafed00d") 211 212 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 213 require.NoError(t, err) 214 want := links[10:13] 215 assert.Equal(t, want, got) 216 assert.Equal(t, entities.PageInfo{ 217 HasNextPage: true, 218 HasPreviousPage: false, 219 StartCursor: want[0].Cursor().Encode(), 220 EndCursor: want[2].Cursor().Encode(), 221 }, pageInfo) 222 } 223 224 func testStakeLinkingPaginationLast(t *testing.T) { 225 ctx := tempTransaction(t) 226 227 ls, links := setupStakeLinkingPaginationTest(t, ctx) 228 229 last := int32(3) 230 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false) 231 require.NoError(t, err) 232 partyID := entities.PartyID("cafed00d") 233 234 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 235 require.NoError(t, err) 236 want := links[17:] 237 assert.Equal(t, want, got) 238 assert.Equal(t, entities.PageInfo{ 239 HasNextPage: false, 240 HasPreviousPage: true, 241 StartCursor: want[0].Cursor().Encode(), 242 EndCursor: want[2].Cursor().Encode(), 243 }, pageInfo) 244 } 245 246 func testStakeLinkingPaginationFirstAndAfter(t *testing.T) { 247 ctx := tempTransaction(t) 248 249 ls, links := setupStakeLinkingPaginationTest(t, ctx) 250 251 first := int32(3) 252 after := links[12].Cursor().Encode() 253 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false) 254 require.NoError(t, err) 255 partyID := entities.PartyID("cafed00d") 256 257 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 258 require.NoError(t, err) 259 want := links[13:16] 260 assert.Equal(t, want, got) 261 assert.Equal(t, entities.PageInfo{ 262 HasNextPage: true, 263 HasPreviousPage: true, 264 StartCursor: want[0].Cursor().Encode(), 265 EndCursor: want[2].Cursor().Encode(), 266 }, pageInfo) 267 } 268 269 func testStakeLinkingPaginationLastAndBefore(t *testing.T) { 270 ctx := tempTransaction(t) 271 272 ls, links := setupStakeLinkingPaginationTest(t, ctx) 273 274 last := int32(3) 275 before := links[17].Cursor().Encode() 276 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false) 277 require.NoError(t, err) 278 partyID := entities.PartyID("cafed00d") 279 280 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 281 require.NoError(t, err) 282 want := links[14:17] 283 assert.Equal(t, want, got) 284 assert.Equal(t, entities.PageInfo{ 285 HasNextPage: true, 286 HasPreviousPage: true, 287 StartCursor: want[0].Cursor().Encode(), 288 EndCursor: want[2].Cursor().Encode(), 289 }, pageInfo) 290 } 291 292 func testStakeLinkingPaginationNoPaginationNewestFirst(t *testing.T) { 293 ctx := tempTransaction(t) 294 295 ls, links := setupStakeLinkingPaginationTest(t, ctx) 296 297 pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, true) 298 require.NoError(t, err) 299 partyID := entities.PartyID("cafed00d") 300 301 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 302 require.NoError(t, err) 303 want := entities.ReverseSlice(links[10:]) 304 assert.Equal(t, want, got) 305 assert.Equal(t, entities.PageInfo{ 306 HasNextPage: false, 307 HasPreviousPage: false, 308 StartCursor: want[0].Cursor().Encode(), 309 EndCursor: want[9].Cursor().Encode(), 310 }, pageInfo) 311 } 312 313 func testStakeLinkingPaginationFirstNewestFirst(t *testing.T) { 314 ctx := tempTransaction(t) 315 316 ls, links := setupStakeLinkingPaginationTest(t, ctx) 317 318 first := int32(3) 319 pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, true) 320 require.NoError(t, err) 321 partyID := entities.PartyID("cafed00d") 322 323 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 324 require.NoError(t, err) 325 want := entities.ReverseSlice(links[10:])[:3] 326 assert.Equal(t, want, got) 327 assert.Equal(t, entities.PageInfo{ 328 HasNextPage: true, 329 HasPreviousPage: false, 330 StartCursor: want[0].Cursor().Encode(), 331 EndCursor: want[2].Cursor().Encode(), 332 }, pageInfo) 333 } 334 335 func testStakeLinkingPaginationLastNewestFirst(t *testing.T) { 336 ctx := tempTransaction(t) 337 338 ls, links := setupStakeLinkingPaginationTest(t, ctx) 339 340 last := int32(3) 341 pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, true) 342 require.NoError(t, err) 343 partyID := entities.PartyID("cafed00d") 344 345 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 346 require.NoError(t, err) 347 want := entities.ReverseSlice(links[10:])[7:] 348 assert.Equal(t, want, got) 349 assert.Equal(t, entities.PageInfo{ 350 HasNextPage: false, 351 HasPreviousPage: true, 352 StartCursor: want[0].Cursor().Encode(), 353 EndCursor: want[2].Cursor().Encode(), 354 }, pageInfo) 355 } 356 357 func testStakeLinkingPaginationFirstAndAfterNewestFirst(t *testing.T) { 358 ctx := tempTransaction(t) 359 360 ls, links := setupStakeLinkingPaginationTest(t, ctx) 361 362 first := int32(3) 363 after := links[17].Cursor().Encode() 364 pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, true) 365 require.NoError(t, err) 366 partyID := entities.PartyID("cafed00d") 367 368 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 369 require.NoError(t, err) 370 want := entities.ReverseSlice(links[10:])[3:6] 371 assert.Equal(t, want, got) 372 assert.Equal(t, entities.PageInfo{ 373 HasNextPage: true, 374 HasPreviousPage: true, 375 StartCursor: want[0].Cursor().Encode(), 376 EndCursor: want[2].Cursor().Encode(), 377 }, pageInfo) 378 } 379 380 func testStakeLinkingPaginationLastAndBeforeNewestFirst(t *testing.T) { 381 ctx := tempTransaction(t) 382 383 ls, links := setupStakeLinkingPaginationTest(t, ctx) 384 385 last := int32(3) 386 before := links[12].Cursor().Encode() 387 pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, true) 388 require.NoError(t, err) 389 partyID := entities.PartyID("cafed00d") 390 391 _, got, pageInfo, err := ls.GetStake(ctx, partyID, pagination) 392 require.NoError(t, err) 393 want := entities.ReverseSlice(links[10:])[4:7] 394 assert.Equal(t, want, got) 395 assert.Equal(t, entities.PageInfo{ 396 HasNextPage: true, 397 HasPreviousPage: true, 398 StartCursor: want[0].Cursor().Encode(), 399 EndCursor: want[2].Cursor().Encode(), 400 }, pageInfo) 401 } 402 403 func addStakeLinking(t *testing.T, ctx context.Context, ls *sqlstore.StakeLinking, id string, partyID string, logIndex int64, block entities.Block) entities.StakeLinking { 404 t.Helper() 405 l := entities.StakeLinking{ 406 ID: entities.StakeLinkingID(id), 407 StakeLinkingType: entities.StakeLinkingTypeLink, 408 EthereumTimestamp: block.VegaTime, 409 PartyID: entities.PartyID(partyID), 410 Amount: decimal.NewFromFloat(1), 411 StakeLinkingStatus: entities.StakeLinkingStatusAccepted, 412 FinalizedAt: block.VegaTime, 413 ForeignTxHash: GenerateID(), 414 LogIndex: logIndex, 415 EthereumAddress: "0xfe179560b9d0cc44c5fea54c2167c1cee7ccfcabf294752a4f43fb64ddffda85", 416 VegaTime: block.VegaTime, 417 } 418 419 ls.Upsert(ctx, &l) 420 421 return l 422 } 423 424 func setupStakeLinkingPaginationTest(t *testing.T, ctx context.Context) (*sqlstore.StakeLinking, []entities.StakeLinking) { 425 t.Helper() 426 bs := sqlstore.NewBlocks(connectionSource) 427 ls := sqlstore.NewStakeLinking(connectionSource) 428 429 blockTime := time.Date(2022, 7, 27, 8, 0, 0, 0, time.Local) 430 linkings := make([]entities.StakeLinking, 20) 431 432 partyID := "cafed00d" 433 for i := 0; i < 10; i++ { 434 blockTime = blockTime.Add(time.Minute) 435 block := addTestBlockForTime(t, ctx, bs, blockTime) 436 id := int64(i + 1) 437 linkingID := fmt.Sprintf("deadbeef%02d", id) 438 439 linkings[i] = addStakeLinking(t, ctx, ls, linkingID, partyID, id, block) 440 } 441 442 for i := 0; i < 10; i++ { 443 blockTime = blockTime.Add(time.Minute) 444 block := addTestBlockForTime(t, ctx, bs, blockTime) 445 id := int64(i + 1) 446 linkingID := fmt.Sprintf("deadbeef%02d", id) 447 linkings[10+i] = addStakeLinking(t, ctx, ls, linkingID, partyID, id+10, block) 448 } 449 return ls, linkings 450 } 451 452 func TestStakeLinkingEnums(t *testing.T) { 453 t.Run("should be able to store and retrieve all stake linking statuses", testStakeLinkingStatusEnum) 454 t.Run("should be able to store and retrieve all stake linking types", testStakeLinkingTypeEnum) 455 } 456 457 func testStakeLinkingStatusEnum(t *testing.T) { 458 var stakeLinkingStatus eventspb.StakeLinking_Status 459 states := getEnums(t, stakeLinkingStatus) 460 assert.Len(t, states, 4) 461 for s, state := range states { 462 t.Run(state, func(t *testing.T) { 463 ctx := tempTransaction(t) 464 465 bs, sl := setupStakeLinkingTest(t) 466 467 block := addTestBlock(t, ctx, bs) 468 stakingProtos := getStakingProtos() 469 470 proto := stakingProtos[0] 471 proto.Status = eventspb.StakeLinking_Status(s) 472 data, err := entities.StakeLinkingFromProto(proto, generateTxHash(), block.VegaTime) 473 require.NoError(t, err) 474 assert.NoError(t, sl.Upsert(ctx, data)) 475 476 _, got, _, err := sl.GetStake(ctx, data.PartyID, entities.CursorPagination{}) 477 require.NoError(t, err) 478 assert.Len(t, got, 1) 479 assert.Equal(t, data.StakeLinkingStatus, got[0].StakeLinkingStatus) 480 }) 481 } 482 } 483 484 func testStakeLinkingTypeEnum(t *testing.T) { 485 var stakeLinkingType eventspb.StakeLinking_Type 486 types := getEnums(t, stakeLinkingType) 487 assert.Len(t, types, 3) 488 for ty, typ := range types { 489 t.Run(typ, func(t *testing.T) { 490 ctx := tempTransaction(t) 491 492 bs, sl := setupStakeLinkingTest(t) 493 494 block := addTestBlock(t, ctx, bs) 495 stakingProtos := getStakingProtos() 496 497 proto := stakingProtos[0] 498 proto.Type = eventspb.StakeLinking_Type(ty) 499 data, err := entities.StakeLinkingFromProto(proto, generateTxHash(), block.VegaTime) 500 require.NoError(t, err) 501 assert.NoError(t, sl.Upsert(ctx, data)) 502 503 var got entities.StakeLinking 504 505 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got, "SELECT * FROM stake_linking where tx_hash = $1", data.TxHash)) 506 assert.Equal(t, data.StakeLinkingType, got.StakeLinkingType) 507 }) 508 } 509 }