code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/node_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  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/datanode/entities"
    24  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    25  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    26  
    27  	"github.com/georgysavva/scany/pgxscan"
    28  	"github.com/shopspring/decimal"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func addTestNode(t *testing.T, ctx context.Context, ps *sqlstore.Node, block entities.Block, id string) entities.Node {
    34  	t.Helper()
    35  	node := entities.Node{
    36  		ID:              entities.NodeID(id),
    37  		PubKey:          entities.VegaPublicKey(GenerateID()),
    38  		TmPubKey:        entities.TendermintPublicKey(generateTendermintPublicKey()),
    39  		EthereumAddress: entities.EthereumAddress(generateEthereumAddress()),
    40  		VegaTime:        block.VegaTime,
    41  		Status:          entities.NodeStatusNonValidator,
    42  		TxHash:          generateTxHash(),
    43  	}
    44  
    45  	err := ps.UpsertNode(ctx, &node)
    46  	require.NoError(t, err)
    47  	return node
    48  }
    49  
    50  func addNodeAnnounced(t *testing.T, ctx context.Context, ps *sqlstore.Node, nodeID entities.NodeID, added bool, epochSeq uint64, vegatime time.Time) {
    51  	t.Helper()
    52  	aux := entities.ValidatorUpdateAux{
    53  		Added:    added,
    54  		EpochSeq: epochSeq,
    55  	}
    56  	err := ps.AddNodeAnnouncedEvent(ctx, nodeID.String(), vegatime, &aux)
    57  	require.NoError(t, err)
    58  }
    59  
    60  func addRankingScore(t *testing.T, ctx context.Context, ps *sqlstore.Node, node entities.Node, r entities.RankingScore) {
    61  	t.Helper()
    62  
    63  	aux := entities.RankingScoreAux{
    64  		NodeID:   node.ID,
    65  		EpochSeq: r.EpochSeq,
    66  	}
    67  
    68  	err := ps.UpsertRanking(ctx, &r, &aux)
    69  	require.NoError(t, err)
    70  }
    71  
    72  func TestUpdateNodePubKey(t *testing.T) {
    73  	ctx := tempTransaction(t)
    74  
    75  	bs := sqlstore.NewBlocks(connectionSource)
    76  	ns := sqlstore.NewNode(connectionSource)
    77  	block := addTestBlock(t, ctx, bs)
    78  
    79  	now := time.Now()
    80  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
    81  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 0, now)
    82  
    83  	kr := entities.KeyRotation{
    84  		NodeID:    node1.ID,
    85  		OldPubKey: node1.PubKey,
    86  		NewPubKey: entities.VegaPublicKey(GenerateID()),
    87  		VegaTime:  block.VegaTime,
    88  	}
    89  
    90  	ns.UpdatePublicKey(ctx, &kr)
    91  
    92  	fetched, err := ns.GetNodeByID(ctx, node1.ID.String(), 1)
    93  	assert.NoError(t, err)
    94  	assert.Equal(t, fetched.PubKey, kr.NewPubKey)
    95  }
    96  
    97  func TestGetNodes(t *testing.T) {
    98  	ctx := tempTransaction(t)
    99  
   100  	bs := sqlstore.NewBlocks(connectionSource)
   101  	ns := sqlstore.NewNode(connectionSource)
   102  	block := addTestBlock(t, ctx, bs)
   103  
   104  	now := time.Now()
   105  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
   106  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 0, now)
   107  	addNodeAnnounced(t, ctx, ns, node1.ID, false, 7, now)
   108  	addRankingScore(t, ctx, ns, node1,
   109  		entities.RankingScore{
   110  			StakeScore:       decimal.NewFromFloat(0.5),
   111  			PerformanceScore: decimal.NewFromFloat(0.25),
   112  			PreviousStatus:   entities.ValidatorNodeStatusErsatz,
   113  			Status:           entities.ValidatorNodeStatusTendermint,
   114  			EpochSeq:         3,
   115  			VegaTime:         block.VegaTime,
   116  		})
   117  
   118  	// get all nodes
   119  	found, _, err := ns.GetNodes(ctx, 3, entities.CursorPagination{})
   120  	require.NoError(t, err)
   121  	require.Len(t, found, 1)
   122  
   123  	// get all nodes
   124  	found, _, err = ns.GetNodes(ctx, 7, entities.CursorPagination{})
   125  	require.NoError(t, err)
   126  	require.Len(t, found, 0)
   127  
   128  	// get single node in epoch where it had a ranking
   129  	node, err := ns.GetNodeByID(ctx, node1.ID.String(), 3)
   130  	require.NoError(t, err)
   131  	require.NotNil(t, node)
   132  	require.NotNil(t, node.RankingScore)
   133  
   134  	node, err = ns.GetNodeByID(ctx, "DEADBEEF", 3)
   135  	require.Error(t, err)
   136  
   137  	// check the value can be changed, since this happens during a checkpoint restore
   138  	// we were need to remove genesis validators if they aren't in the checkpoint
   139  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 7, now)
   140  	// get all nodes
   141  	found, _, err = ns.GetNodes(ctx, 7, entities.CursorPagination{})
   142  	require.NoError(t, err)
   143  	require.Len(t, found, 1)
   144  }
   145  
   146  func TestNodeGetByTxHash(t *testing.T) {
   147  	ctx := tempTransaction(t)
   148  
   149  	bs := sqlstore.NewBlocks(connectionSource)
   150  	ns := sqlstore.NewNode(connectionSource)
   151  	block := addTestBlock(t, ctx, bs)
   152  
   153  	now := time.Now()
   154  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
   155  	node2 := addTestNode(t, ctx, ns, block, GenerateID())
   156  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 0, now)
   157  	addNodeAnnounced(t, ctx, ns, node2.ID, false, 7, now)
   158  	addNodeAnnounced(t, ctx, ns, node2.ID, false, 9, now)
   159  
   160  	found, err := ns.GetByTxHash(ctx, node1.TxHash)
   161  	require.NoError(t, err)
   162  	require.Len(t, found, 1)
   163  	require.Equal(t, node1.ID, found[0].ID)
   164  
   165  	found, err = ns.GetByTxHash(ctx, node2.TxHash)
   166  	require.NoError(t, err)
   167  	require.Len(t, found, 1)
   168  	require.Equal(t, node2.ID, found[0].ID)
   169  }
   170  
   171  func TestGetNodesJoiningAndLeaving(t *testing.T) {
   172  	ctx := tempTransaction(t)
   173  
   174  	bs := sqlstore.NewBlocks(connectionSource)
   175  	ns := sqlstore.NewNode(connectionSource)
   176  	block := addTestBlock(t, ctx, bs)
   177  
   178  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
   179  	node2 := addTestNode(t, ctx, ns, block, GenerateID())
   180  
   181  	// The node1 will exist int the epochs [2,3] and [6,7]
   182  	exists := map[int]bool{2: true, 3: true, 6: true, 7: true}
   183  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 2, time.Now())
   184  	addNodeAnnounced(t, ctx, ns, node1.ID, false, 4, time.Now())
   185  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 6, time.Now())
   186  	addNodeAnnounced(t, ctx, ns, node1.ID, false, 8, time.Now())
   187  
   188  	// node2 will always exist
   189  	addNodeAnnounced(t, ctx, ns, node2.ID, true, 0, time.Now())
   190  
   191  	nodeID1 := node1.ID.String()
   192  	nodeID2 := node2.ID.String()
   193  
   194  	assertNodeExistence(ctx, t, ns, nodeID1, 1, false)
   195  	assertNodeExistence(ctx, t, ns, nodeID2, 1, true)
   196  	for i := 1; i < 10; i++ {
   197  		assertNodeExistence(ctx, t, ns, nodeID1, uint64(i), exists[i])
   198  		assertNodeExistence(ctx, t, ns, nodeID2, uint64(i), true)
   199  	}
   200  }
   201  
   202  func TestGetNodeData(t *testing.T) {
   203  	ctx := tempTransaction(t)
   204  
   205  	bs := sqlstore.NewBlocks(connectionSource)
   206  	ns := sqlstore.NewNode(connectionSource)
   207  	es := sqlstore.NewEpochs(connectionSource)
   208  	ds := sqlstore.NewDelegations(connectionSource)
   209  	ps := sqlstore.NewParties(connectionSource)
   210  
   211  	block := addTestBlock(t, ctx, bs)
   212  	party1 := addTestParty(t, ctx, ps, block)
   213  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
   214  	node2 := addTestNode(t, ctx, ns, block, GenerateID())
   215  
   216  	addTestDelegation(t, ctx, ds, party1, node1, 3, block, 0)
   217  	addTestDelegation(t, ctx, ds, party1, node1, 4, block, 1)
   218  	addTestDelegation(t, ctx, ds, party1, node2, 3, block, 2)
   219  	addTestDelegation(t, ctx, ds, party1, node2, 4, block, 3)
   220  
   221  	// node1 goes from pending -> ersatz -> tendermint
   222  	// then gets demoted straight to pending with a zero perf score
   223  	addRankingScore(t, ctx, ns, node1,
   224  		entities.RankingScore{
   225  			StakeScore:       decimal.NewFromFloat(0.5),
   226  			PerformanceScore: decimal.NewFromFloat(0.25),
   227  			PreviousStatus:   entities.ValidatorNodeStatusPending,
   228  			Status:           entities.ValidatorNodeStatusErsatz,
   229  			EpochSeq:         2,
   230  			VegaTime:         block.VegaTime,
   231  		})
   232  	addRankingScore(t, ctx, ns, node1,
   233  		entities.RankingScore{
   234  			StakeScore:       decimal.NewFromFloat(0.5),
   235  			PerformanceScore: decimal.NewFromFloat(0.25),
   236  			PreviousStatus:   entities.ValidatorNodeStatusErsatz,
   237  			Status:           entities.ValidatorNodeStatusTendermint,
   238  			EpochSeq:         3,
   239  			VegaTime:         block.VegaTime,
   240  		})
   241  	addRankingScore(t, ctx, ns, node1,
   242  		entities.RankingScore{
   243  			StakeScore:       decimal.NewFromFloat(0.5),
   244  			PerformanceScore: decimal.Zero,
   245  			PreviousStatus:   entities.ValidatorNodeStatusTendermint,
   246  			Status:           entities.ValidatorNodeStatusPending,
   247  			EpochSeq:         4,
   248  			VegaTime:         block.VegaTime,
   249  		})
   250  
   251  	// node 2 is always a happy tendermint node
   252  	for i := 0; i < 6; i++ {
   253  		addRankingScore(t, ctx, ns, node2, entities.RankingScore{
   254  			StakeScore:       decimal.NewFromFloat(0.5),
   255  			PerformanceScore: decimal.NewFromFloat(0.25),
   256  			PreviousStatus:   entities.ValidatorNodeStatusTendermint,
   257  			Status:           entities.ValidatorNodeStatusTendermint,
   258  			EpochSeq:         uint64(i),
   259  			VegaTime:         block.VegaTime,
   260  		})
   261  	}
   262  
   263  	// The node1 will exist int the epochs [2,3,4]
   264  	addNodeAnnounced(t, ctx, ns, node1.ID, true, 2, time.Now())
   265  	addNodeAnnounced(t, ctx, ns, node1.ID, false, 5, time.Now())
   266  
   267  	// node2 will always exist
   268  	addNodeAnnounced(t, ctx, ns, node2.ID, true, 0, time.Now())
   269  
   270  	// move to epoch 2 both nodes should exist
   271  	now := time.Unix(2000, 4)
   272  	addTestEpoch(t, ctx, es, 2, now, now, &now, block)
   273  	nodeData, err := ns.GetNodeData(ctx, 2)
   274  	require.NoError(t, err)
   275  	require.Equal(t, uint32(2), nodeData.TotalNodes)
   276  	require.Equal(t, entities.NodeSet{
   277  		Total: 1,
   278  	}, nodeData.TendermintNodes)
   279  	require.Equal(t, entities.NodeSet{
   280  		Total:    1,
   281  		Promoted: []string{node1.ID.String()},
   282  	}, nodeData.ErsatzNodes)
   283  	require.Equal(t, entities.NodeSet{}, nodeData.PendingNodes)
   284  
   285  	// move to epoch 3 and check promotions
   286  	addTestEpoch(t, ctx, es, 3, now, now, &now, block)
   287  	nodeData, err = ns.GetNodeData(ctx, 3)
   288  	require.NoError(t, err)
   289  	require.Equal(t, uint32(2), nodeData.TotalNodes)
   290  	require.Equal(t, entities.NodeSet{
   291  		Total:    2,
   292  		Promoted: []string{node1.ID.String()},
   293  	}, nodeData.TendermintNodes)
   294  	require.Equal(t, entities.NodeSet{}, nodeData.ErsatzNodes)
   295  	require.Equal(t, entities.NodeSet{}, nodeData.PendingNodes)
   296  
   297  	// move to epoch 4 and check demotions
   298  	now = now.Add(time.Hour)
   299  	addTestEpoch(t, ctx, es, 4, now, now, &now, block)
   300  	nodeData, err = ns.GetNodeData(ctx, 4)
   301  	require.NoError(t, err)
   302  	require.Equal(t, uint32(2), nodeData.TotalNodes)
   303  	require.Equal(t, uint32(1), nodeData.InactiveNodes)
   304  	require.Equal(t, entities.NodeSet{
   305  		Total: 1,
   306  	}, nodeData.TendermintNodes)
   307  	require.Equal(t, entities.NodeSet{}, nodeData.ErsatzNodes)
   308  	require.Equal(t, entities.NodeSet{
   309  		Total:    1,
   310  		Inactive: 1,
   311  		Demoted:  []string{node1.ID.String()},
   312  	}, nodeData.PendingNodes)
   313  
   314  	// move to epoch 5 just have one tendermint node
   315  	now = now.Add(time.Hour)
   316  	addTestEpoch(t, ctx, es, 5, now, now, &now, block)
   317  	nodeData, err = ns.GetNodeData(ctx, 5)
   318  	require.NoError(t, err)
   319  	require.Equal(t, uint32(1), nodeData.TotalNodes)
   320  	require.Equal(t, entities.NodeSet{
   321  		Total: 1,
   322  	}, nodeData.TendermintNodes)
   323  	require.Equal(t, entities.NodeSet{}, nodeData.ErsatzNodes)
   324  	require.Equal(t, entities.NodeSet{}, nodeData.PendingNodes)
   325  }
   326  
   327  func assertNodeExistence(ctx context.Context, t *testing.T, ns *sqlstore.Node, nodeID string, epoch uint64, exists bool) {
   328  	t.Helper()
   329  	nodes, _, err := ns.GetNodes(ctx, epoch, entities.CursorPagination{})
   330  	require.NoError(t, err)
   331  	node, err := ns.GetNodeByID(ctx, nodeID, epoch)
   332  
   333  	found := false
   334  	for _, n := range nodes {
   335  		if n.ID.String() == nodeID {
   336  			found = true
   337  			break
   338  		}
   339  	}
   340  
   341  	if !exists {
   342  		require.ErrorIs(t, err, entities.ErrNotFound)
   343  		require.False(t, found)
   344  		return
   345  	}
   346  
   347  	require.NoError(t, err)
   348  	require.True(t, found)
   349  	require.Equal(t, node.ID.String(), nodeID)
   350  }
   351  
   352  func TestNodePagination(t *testing.T) {
   353  	t.Run("Should return all nodes if no pagination is specified", testNodePaginationNoPagination)
   354  	t.Run("Should return first page of results if first is provided", testNodePaginationFirst)
   355  	t.Run("Should return last page of results if last is provided", testNodePaginationLast)
   356  	t.Run("Should return requested page of results if first and after is provided", testNodePaginationFirstAfter)
   357  	t.Run("Should return requested page of results if last and before is provided", testNodePaginationLastBefore)
   358  }
   359  
   360  func addPaginationTestNodes(t *testing.T, ctx context.Context, ns *sqlstore.Node) (nodes []entities.Node) {
   361  	t.Helper()
   362  	blockTime := time.Now().Add(-time.Hour)
   363  	bs := sqlstore.NewBlocks(connectionSource)
   364  
   365  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef01"))
   366  	blockTime = blockTime.Add(time.Minute)
   367  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef02"))
   368  	blockTime = blockTime.Add(time.Minute)
   369  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef03"))
   370  	blockTime = blockTime.Add(time.Minute)
   371  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef04"))
   372  	blockTime = blockTime.Add(time.Minute)
   373  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef05"))
   374  	blockTime = blockTime.Add(time.Minute)
   375  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef06"))
   376  	blockTime = blockTime.Add(time.Minute)
   377  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef07"))
   378  	blockTime = blockTime.Add(time.Minute)
   379  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef08"))
   380  	blockTime = blockTime.Add(time.Minute)
   381  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef09"))
   382  	blockTime = blockTime.Add(time.Minute)
   383  	nodes = append(nodes, addTestNode(t, ctx, ns, addTestBlockForTime(t, ctx, bs, blockTime), "deadbeef10"))
   384  	addNodeAnnounced(t, ctx, ns, nodes[0].ID, true, 1, nodes[0].VegaTime)
   385  	addNodeAnnounced(t, ctx, ns, nodes[1].ID, true, 1, nodes[1].VegaTime)
   386  	addNodeAnnounced(t, ctx, ns, nodes[2].ID, true, 1, nodes[2].VegaTime)
   387  	addNodeAnnounced(t, ctx, ns, nodes[3].ID, true, 1, nodes[3].VegaTime)
   388  	addNodeAnnounced(t, ctx, ns, nodes[4].ID, true, 1, nodes[4].VegaTime)
   389  	addNodeAnnounced(t, ctx, ns, nodes[5].ID, true, 1, nodes[5].VegaTime)
   390  	addNodeAnnounced(t, ctx, ns, nodes[6].ID, true, 1, nodes[6].VegaTime)
   391  	addNodeAnnounced(t, ctx, ns, nodes[7].ID, true, 1, nodes[7].VegaTime)
   392  	addNodeAnnounced(t, ctx, ns, nodes[8].ID, true, 1, nodes[8].VegaTime)
   393  	addNodeAnnounced(t, ctx, ns, nodes[9].ID, true, 1, nodes[9].VegaTime)
   394  
   395  	return nodes
   396  }
   397  
   398  func testNodePaginationNoPagination(t *testing.T) {
   399  	ctx := tempTransaction(t)
   400  
   401  	ns := sqlstore.NewNode(connectionSource)
   402  	nodes := addPaginationTestNodes(t, ctx, ns)
   403  
   404  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   405  	require.NoError(t, err)
   406  
   407  	got, pageInfo, err := ns.GetNodes(ctx, 1, pagination)
   408  	require.NoError(t, err)
   409  	assert.Len(t, got, len(nodes))
   410  	assert.Equal(t, nodes[0].ID, got[0].ID)
   411  	assert.Equal(t, nodes[1].ID, got[1].ID)
   412  	assert.Equal(t, nodes[2].ID, got[2].ID)
   413  	assert.Equal(t, nodes[3].ID, got[3].ID)
   414  	assert.Equal(t, nodes[4].ID, got[4].ID)
   415  	assert.Equal(t, nodes[5].ID, got[5].ID)
   416  	assert.Equal(t, nodes[6].ID, got[6].ID)
   417  	assert.Equal(t, nodes[7].ID, got[7].ID)
   418  	assert.Equal(t, nodes[8].ID, got[8].ID)
   419  	assert.Equal(t, nodes[9].ID, got[9].ID)
   420  	assert.Equal(t, entities.PageInfo{
   421  		HasNextPage:     false,
   422  		HasPreviousPage: false,
   423  		StartCursor:     nodes[0].Cursor().Encode(),
   424  		EndCursor:       nodes[9].Cursor().Encode(),
   425  	}, pageInfo)
   426  }
   427  
   428  func testNodePaginationFirst(t *testing.T) {
   429  	ctx := tempTransaction(t)
   430  
   431  	ns := sqlstore.NewNode(connectionSource)
   432  	nodes := addPaginationTestNodes(t, ctx, ns)
   433  	first := int32(3)
   434  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false)
   435  	require.NoError(t, err)
   436  
   437  	got, pageInfo, err := ns.GetNodes(ctx, 1, pagination)
   438  	require.NoError(t, err)
   439  	assert.Len(t, got, 3)
   440  	assert.Equal(t, nodes[0].ID, got[0].ID)
   441  	assert.Equal(t, nodes[1].ID, got[1].ID)
   442  	assert.Equal(t, nodes[2].ID, got[2].ID)
   443  	assert.Equal(t, entities.PageInfo{
   444  		HasNextPage:     true,
   445  		HasPreviousPage: false,
   446  		StartCursor:     nodes[0].Cursor().Encode(),
   447  		EndCursor:       nodes[2].Cursor().Encode(),
   448  	}, pageInfo)
   449  }
   450  
   451  func testNodePaginationLast(t *testing.T) {
   452  	ctx := tempTransaction(t)
   453  
   454  	ns := sqlstore.NewNode(connectionSource)
   455  	nodes := addPaginationTestNodes(t, ctx, ns)
   456  
   457  	last := int32(3)
   458  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false)
   459  	require.NoError(t, err)
   460  
   461  	got, pageInfo, err := ns.GetNodes(ctx, 1, pagination)
   462  	require.NoError(t, err)
   463  	assert.Len(t, got, 3)
   464  	assert.Equal(t, nodes[7].ID, got[0].ID)
   465  	assert.Equal(t, nodes[8].ID, got[1].ID)
   466  	assert.Equal(t, nodes[9].ID, got[2].ID)
   467  	assert.Equal(t, entities.PageInfo{
   468  		HasNextPage:     false,
   469  		HasPreviousPage: true,
   470  		StartCursor:     nodes[7].Cursor().Encode(),
   471  		EndCursor:       nodes[9].Cursor().Encode(),
   472  	}, pageInfo)
   473  }
   474  
   475  func testNodePaginationFirstAfter(t *testing.T) {
   476  	ctx := tempTransaction(t)
   477  
   478  	ns := sqlstore.NewNode(connectionSource)
   479  	nodes := addPaginationTestNodes(t, ctx, ns)
   480  
   481  	first := int32(3)
   482  	after := nodes[2].Cursor().Encode()
   483  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
   484  	require.NoError(t, err)
   485  
   486  	got, pageInfo, err := ns.GetNodes(ctx, 1, pagination)
   487  	require.NoError(t, err)
   488  	assert.Len(t, got, 3)
   489  	assert.Equal(t, nodes[3].ID, got[0].ID)
   490  	assert.Equal(t, nodes[4].ID, got[1].ID)
   491  	assert.Equal(t, nodes[5].ID, got[2].ID)
   492  	assert.Equal(t, entities.PageInfo{
   493  		HasNextPage:     true,
   494  		HasPreviousPage: true,
   495  		StartCursor:     nodes[3].Cursor().Encode(),
   496  		EndCursor:       nodes[5].Cursor().Encode(),
   497  	}, pageInfo)
   498  }
   499  
   500  func testNodePaginationLastBefore(t *testing.T) {
   501  	ctx := tempTransaction(t)
   502  
   503  	ns := sqlstore.NewNode(connectionSource)
   504  	nodes := addPaginationTestNodes(t, ctx, ns)
   505  
   506  	last := int32(3)
   507  	before := nodes[7].Cursor().Encode()
   508  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false)
   509  	require.NoError(t, err)
   510  
   511  	got, pageInfo, err := ns.GetNodes(ctx, 1, pagination)
   512  	require.NoError(t, err)
   513  	assert.Len(t, got, 3)
   514  	assert.Equal(t, nodes[4].ID, got[0].ID)
   515  	assert.Equal(t, nodes[5].ID, got[1].ID)
   516  	assert.Equal(t, nodes[6].ID, got[2].ID)
   517  	assert.Equal(t, entities.PageInfo{
   518  		HasNextPage:     true,
   519  		HasPreviousPage: true,
   520  		StartCursor:     nodes[4].Cursor().Encode(),
   521  		EndCursor:       nodes[6].Cursor().Encode(),
   522  	}, pageInfo)
   523  }
   524  
   525  func TestNode_AddRankingScoreInSameEpoch(t *testing.T) {
   526  	ctx := tempTransaction(t)
   527  
   528  	bs := sqlstore.NewBlocks(connectionSource)
   529  	ns := sqlstore.NewNode(connectionSource)
   530  
   531  	block := addTestBlock(t, ctx, bs)
   532  	node1 := addTestNode(t, ctx, ns, block, GenerateID())
   533  
   534  	// node1 goes from pending -> ersatz -> tendermint
   535  	// then gets demoted straight to pending with a zero perf score
   536  	addRankingScore(t, ctx, ns, node1,
   537  		entities.RankingScore{
   538  			StakeScore:       decimal.NewFromFloat(0.5),
   539  			PerformanceScore: decimal.NewFromFloat(0.25),
   540  			PreviousStatus:   entities.ValidatorNodeStatusPending,
   541  			Status:           entities.ValidatorNodeStatusErsatz,
   542  			EpochSeq:         2,
   543  			VegaTime:         block.VegaTime,
   544  		})
   545  	addRankingScore(t, ctx, ns, node1,
   546  		entities.RankingScore{
   547  			StakeScore:       decimal.NewFromFloat(0.5),
   548  			PerformanceScore: decimal.Zero,
   549  			PreviousStatus:   entities.ValidatorNodeStatusTendermint,
   550  			Status:           entities.ValidatorNodeStatusPending,
   551  			EpochSeq:         2,
   552  			VegaTime:         block.VegaTime,
   553  		})
   554  }
   555  
   556  func TestNodeStatusEnum(t *testing.T) {
   557  	var nodeStatus vegapb.NodeStatus
   558  	states := getEnums(t, nodeStatus)
   559  	assert.Len(t, states, 3)
   560  	for s, state := range states {
   561  		t.Run(state, func(t *testing.T) {
   562  			ctx := tempTransaction(t)
   563  
   564  			bs := sqlstore.NewBlocks(connectionSource)
   565  			ns := sqlstore.NewNode(connectionSource)
   566  			block := addTestBlock(t, ctx, bs)
   567  
   568  			node := entities.Node{
   569  				ID:              entities.NodeID(GenerateID()),
   570  				PubKey:          entities.VegaPublicKey(GenerateID()),
   571  				TmPubKey:        entities.TendermintPublicKey(generateTendermintPublicKey()),
   572  				EthereumAddress: entities.EthereumAddress(generateEthereumAddress()),
   573  				VegaTime:        block.VegaTime,
   574  				Status:          entities.NodeStatus(s),
   575  				TxHash:          generateTxHash(),
   576  			}
   577  
   578  			require.NoError(t, ns.UpsertNode(ctx, &node))
   579  			fetched, err := ns.GetByTxHash(ctx, node.TxHash)
   580  			assert.NoError(t, err)
   581  			assert.Len(t, fetched, 1)
   582  			assert.Equal(t, node.Status, fetched[0].Status)
   583  		})
   584  	}
   585  }
   586  
   587  func TestNodeValidatorStatusEnum(t *testing.T) {
   588  	var validatorNodeStatus vegapb.ValidatorNodeStatus
   589  	states := getEnums(t, validatorNodeStatus)
   590  	assert.Len(t, states, 4)
   591  
   592  	for s, state := range states {
   593  		t.Run(state, func(t *testing.T) {
   594  			ctx := tempTransaction(t)
   595  
   596  			bs := sqlstore.NewBlocks(connectionSource)
   597  			ns := sqlstore.NewNode(connectionSource)
   598  
   599  			block := addTestBlock(t, ctx, bs)
   600  			node1 := addTestNode(t, ctx, ns, block, GenerateID())
   601  			score := entities.RankingScore{
   602  				StakeScore:       decimal.NewFromFloat(0.5),
   603  				PerformanceScore: decimal.NewFromFloat(0.25),
   604  				PreviousStatus:   entities.ValidatorNodeStatusUnspecified,
   605  				Status:           entities.ValidatorNodeStatus(s),
   606  				EpochSeq:         1,
   607  				VegaTime:         block.VegaTime,
   608  				TxHash:           generateTxHash(),
   609  			}
   610  			addRankingScore(t, ctx, ns, node1, score)
   611  			var got entities.RankingScore
   612  			require.NoError(t, pgxscan.Get(ctx, connectionSource, &got, `
   613  			SELECT
   614  				stake_score,
   615  				performance_score,
   616  				previous_status,
   617  				status,
   618  				voting_power,
   619  				ranking_score,
   620  				tx_hash,
   621  				vega_time
   622  			FROM ranking_scores WHERE tx_hash = $1`, score.TxHash))
   623  			assert.Equal(t, score.Status, got.Status)
   624  		})
   625  	}
   626  }