code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/oracle_data_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  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    24  	"code.vegaprotocol.io/vega/datanode/entities"
    25  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    26  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    27  	datapb "code.vegaprotocol.io/vega/protos/vega/data/v1"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func TestOracleData(t *testing.T) {
    34  	t.Run("Add should insert oracle data", testAddOracleData)
    35  	t.Run("ListOracleData should return all data where matched spec ids contains the provided id", testGetOracleDataBySpecID)
    36  	t.Run("ListOracleData should return all data if the spec id is not provided", testGetOracleDataWithoutSpecID)
    37  	t.Run("GetByTxHash", testGetOracleDataByTxHash)
    38  	t.Run("Add should insert and retrieve oracle data with error", testAddAndRetrieveOracleDataWithError)
    39  	t.Run("Add should insert and retrieve oracle data with meta data", testAddAndRetrieveOracleDataWithMetaData)
    40  }
    41  
    42  func setupOracleDataTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.OracleData, sqlstore.Connection) {
    43  	t.Helper()
    44  	bs := sqlstore.NewBlocks(connectionSource)
    45  	od := sqlstore.NewOracleData(connectionSource)
    46  	return bs, od, connectionSource
    47  }
    48  
    49  func testAddAndRetrieveOracleDataWithError(t *testing.T) {
    50  	ctx := tempTransaction(t)
    51  
    52  	bs, od, conn := setupOracleDataTest(t)
    53  
    54  	var rowCount int
    55  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount))
    56  	assert.Equal(t, 0, rowCount)
    57  
    58  	block := addTestBlock(t, ctx, bs)
    59  	dataProtos := getTestOracleData()
    60  
    61  	for i, proto := range dataProtos {
    62  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
    63  		require.NoError(t, err)
    64  		assert.NoError(t, od.Add(ctx, data))
    65  	}
    66  
    67  	dataForSpec, _, err := od.ListOracleData(ctx, "deadbeef01", entities.CursorPagination{})
    68  	assert.NoError(t, err)
    69  	assert.Equal(t, len(dataForSpec), 1)
    70  
    71  	data := dataForSpec[0]
    72  
    73  	assert.Equal(t, dataProtos[0].ExternalData.Data.Error, data.ExternalData.Data.Error)
    74  }
    75  
    76  func testAddAndRetrieveOracleDataWithMetaData(t *testing.T) {
    77  	ctx := tempTransaction(t)
    78  
    79  	bs, od, conn := setupOracleDataTest(t)
    80  
    81  	var rowCount int
    82  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount))
    83  	assert.Equal(t, 0, rowCount)
    84  
    85  	block := addTestBlock(t, ctx, bs)
    86  	dataProtos := getTestOracleData()
    87  
    88  	for i, proto := range dataProtos {
    89  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
    90  		require.NoError(t, err)
    91  		assert.NoError(t, od.Add(ctx, data))
    92  	}
    93  
    94  	dataForSpec, _, err := od.ListOracleData(ctx, "deadbeef01", entities.CursorPagination{})
    95  	assert.NoError(t, err)
    96  	assert.Equal(t, len(dataForSpec), 1)
    97  
    98  	data := dataForSpec[0]
    99  
   100  	assert.Equal(t, dataProtos[0].ExternalData.Data.MetaData[0].Value, data.ExternalData.Data.MetaData[0].Value)
   101  }
   102  
   103  func testAddOracleData(t *testing.T) {
   104  	ctx := tempTransaction(t)
   105  
   106  	bs, od, conn := setupOracleDataTest(t)
   107  
   108  	var rowCount int
   109  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount))
   110  	assert.Equal(t, 0, rowCount)
   111  
   112  	block := addTestBlock(t, ctx, bs)
   113  	dataProtos := getTestOracleData()
   114  
   115  	for i, proto := range dataProtos {
   116  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
   117  		require.NoError(t, err)
   118  		assert.NoError(t, od.Add(ctx, data))
   119  	}
   120  
   121  	assert.NoError(t, conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount))
   122  	assert.Equal(t, len(dataProtos), rowCount)
   123  }
   124  
   125  func testGetOracleDataBySpecID(t *testing.T) {
   126  	ctx := tempTransaction(t)
   127  
   128  	bs, od, conn := setupOracleDataTest(t)
   129  
   130  	var rowCount int
   131  	err := conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   132  	require.NoError(t, err)
   133  	assert.Equal(t, 0, rowCount)
   134  
   135  	testTime := time.Now()
   136  	dataProtos := getTestOracleData()
   137  
   138  	for i, proto := range dataProtos {
   139  		block := addTestBlockForTime(t, ctx, bs, testTime)
   140  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
   141  		require.NoError(t, err)
   142  		err = od.Add(ctx, data)
   143  		require.NoError(t, err)
   144  		testTime = testTime.Add(time.Minute)
   145  	}
   146  
   147  	err = conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   148  	require.NoError(t, err)
   149  	assert.Equal(t, len(dataProtos), rowCount)
   150  
   151  	got, _, err := od.ListOracleData(ctx, "deadbeef02", entities.CursorPagination{})
   152  	require.NoError(t, err)
   153  	assert.Equal(t, 2, len(got))
   154  }
   155  
   156  func testGetOracleDataWithoutSpecID(t *testing.T) {
   157  	ctx := tempTransaction(t)
   158  
   159  	bs, od, conn := setupOracleDataTest(t)
   160  
   161  	var rowCount int
   162  	err := conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   163  	require.NoError(t, err)
   164  	assert.Equal(t, 0, rowCount)
   165  
   166  	testTime := time.Now()
   167  	dataProtos := getTestOracleData()
   168  
   169  	for i, proto := range dataProtos {
   170  		block := addTestBlockForTime(t, ctx, bs, testTime)
   171  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
   172  		require.NoError(t, err)
   173  		err = od.Add(ctx, data)
   174  		require.NoError(t, err)
   175  		testTime = testTime.Add(time.Minute)
   176  	}
   177  
   178  	err = conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   179  	require.NoError(t, err)
   180  	assert.Equal(t, len(dataProtos), rowCount)
   181  
   182  	got, _, err := od.ListOracleData(ctx, "", entities.CursorPagination{})
   183  	require.NoError(t, err)
   184  	assert.Equal(t, len(dataProtos), len(got))
   185  }
   186  
   187  func testGetOracleDataByTxHash(t *testing.T) {
   188  	ctx := tempTransaction(t)
   189  
   190  	bs, od, conn := setupOracleDataTest(t)
   191  
   192  	var rowCount int
   193  	err := conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   194  	require.NoError(t, err)
   195  	assert.Equal(t, 0, rowCount)
   196  
   197  	testTime := time.Now()
   198  	dataProtos := getTestOracleData()
   199  
   200  	datas := make([]entities.OracleData, 0, len(dataProtos))
   201  	for i, proto := range dataProtos {
   202  		block := addTestBlockForTime(t, ctx, bs, testTime)
   203  		data, err := entities.OracleDataFromProto(proto, generateTxHash(), block.VegaTime, uint64(i))
   204  		require.NoError(t, err)
   205  		err = od.Add(ctx, data)
   206  		require.NoError(t, err)
   207  		testTime = testTime.Add(time.Minute)
   208  
   209  		datas = append(datas, *data)
   210  	}
   211  
   212  	err = conn.QueryRow(ctx, "select count(*) from oracle_data").Scan(&rowCount)
   213  	require.NoError(t, err)
   214  	assert.Equal(t, len(dataProtos), rowCount)
   215  
   216  	foundData, err := od.GetByTxHash(ctx, datas[0].ExternalData.Data.TxHash)
   217  	require.NoError(t, err)
   218  	assert.Equal(t, 1, len(foundData))
   219  	assert.Equal(t, datas[0].ExternalData.Data, foundData[0].ExternalData.Data)
   220  
   221  	foundData2, err := od.GetByTxHash(ctx, datas[1].ExternalData.Data.TxHash)
   222  	require.NoError(t, err)
   223  	assert.Equal(t, 1, len(foundData2))
   224  	assert.Equal(t, datas[1].ExternalData.Data, foundData2[0].ExternalData.Data)
   225  }
   226  
   227  func getTestOracleData() []*vegapb.OracleData {
   228  	pk1 := dstypes.CreateSignerFromString("b105f00d", dstypes.SignerTypePubKey)
   229  	pk2 := dstypes.CreateSignerFromString("baddcafe", dstypes.SignerTypePubKey)
   230  	testError := "testError"
   231  
   232  	return []*vegapb.OracleData{
   233  		{ // 0
   234  			ExternalData: &datapb.ExternalData{
   235  				Data: &datapb.Data{
   236  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   237  					MetaData: []*datapb.Property{
   238  						{
   239  							Name:  "metaKey",
   240  							Value: "metaValue",
   241  						},
   242  					},
   243  					MatchedSpecIds: []string{"deadbeef01"},
   244  					BroadcastAt:    0,
   245  					Error:          &testError,
   246  				},
   247  			},
   248  		},
   249  		// },
   250  		{ // 1
   251  			ExternalData: &datapb.ExternalData{
   252  				Data: &datapb.Data{
   253  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   254  					Data: []*datapb.Property{
   255  						{
   256  							Name:  "Ticker",
   257  							Value: "USDETH",
   258  						},
   259  					},
   260  					MetaData:       []*datapb.Property{},
   261  					MatchedSpecIds: []string{"deadbeef02"},
   262  					BroadcastAt:    0,
   263  				},
   264  			},
   265  		},
   266  		{ // 2
   267  			ExternalData: &datapb.ExternalData{
   268  				Data: &datapb.Data{
   269  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   270  					Data: []*datapb.Property{
   271  						{
   272  							Name:  "Ticker",
   273  							Value: "USDETH",
   274  						},
   275  					},
   276  					MetaData:       []*datapb.Property{},
   277  					MatchedSpecIds: []string{"deadbeef02"},
   278  					BroadcastAt:    0,
   279  				},
   280  			},
   281  		},
   282  		{ // 3
   283  			ExternalData: &datapb.ExternalData{
   284  				Data: &datapb.Data{
   285  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   286  					Data: []*datapb.Property{
   287  						{
   288  							Name:  "Ticker",
   289  							Value: "USDSOL",
   290  						},
   291  					},
   292  					MetaData:       []*datapb.Property{},
   293  					MatchedSpecIds: []string{"deadbeef03"},
   294  					BroadcastAt:    0,
   295  				},
   296  			},
   297  		},
   298  		{ // 4
   299  			ExternalData: &datapb.ExternalData{
   300  				Data: &datapb.Data{
   301  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   302  					Data: []*datapb.Property{
   303  						{
   304  							Name:  "AAAA",
   305  							Value: "AAAA",
   306  						},
   307  					},
   308  					MetaData:       []*datapb.Property{},
   309  					MatchedSpecIds: []string{"deadbeef04"},
   310  				},
   311  			},
   312  		},
   313  		{ // 5
   314  			ExternalData: &datapb.ExternalData{
   315  				Data: &datapb.Data{
   316  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   317  					Data: []*datapb.Property{
   318  						{
   319  							Name:  "BBBB",
   320  							Value: "BBBB",
   321  						},
   322  					},
   323  					MetaData:       []*datapb.Property{},
   324  					MatchedSpecIds: []string{"deadbeef04"},
   325  				},
   326  			},
   327  		},
   328  		{ // 6
   329  			ExternalData: &datapb.ExternalData{
   330  				Data: &datapb.Data{
   331  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   332  					Data: []*datapb.Property{
   333  						{
   334  							Name:  "CCCC",
   335  							Value: "CCCC",
   336  						},
   337  					},
   338  					MetaData:       []*datapb.Property{},
   339  					MatchedSpecIds: []string{"deadbeef04"},
   340  				},
   341  			},
   342  		},
   343  		{ // 7
   344  			ExternalData: &datapb.ExternalData{
   345  				Data: &datapb.Data{
   346  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   347  					Data: []*datapb.Property{
   348  						{
   349  							Name:  "DDDD",
   350  							Value: "DDDD",
   351  						},
   352  					},
   353  					MetaData:       []*datapb.Property{},
   354  					MatchedSpecIds: []string{"deadbeef04"},
   355  				},
   356  			},
   357  		},
   358  		{ // 8
   359  			ExternalData: &datapb.ExternalData{
   360  				Data: &datapb.Data{
   361  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   362  					Data: []*datapb.Property{
   363  						{
   364  							Name:  "EEEE",
   365  							Value: "EEEE",
   366  						},
   367  					},
   368  					MetaData:       []*datapb.Property{},
   369  					MatchedSpecIds: []string{"deadbeef04"},
   370  				},
   371  			},
   372  		},
   373  		{ // 9
   374  			ExternalData: &datapb.ExternalData{
   375  				Data: &datapb.Data{
   376  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   377  					Data: []*datapb.Property{
   378  						{
   379  							Name:  "FFFF",
   380  							Value: "FFFF",
   381  						},
   382  					},
   383  					MetaData:       []*datapb.Property{},
   384  					MatchedSpecIds: []string{"deadbeef04"},
   385  				},
   386  			},
   387  		},
   388  		{ // 10
   389  			ExternalData: &datapb.ExternalData{
   390  				Data: &datapb.Data{
   391  					Signers: []*datapb.Signer{pk1.IntoProto(), pk2.IntoProto()},
   392  					Data: []*datapb.Property{
   393  						{
   394  							Name:  "GGGG",
   395  							Value: "GGGG",
   396  						},
   397  					},
   398  					MetaData:       []*datapb.Property{},
   399  					MatchedSpecIds: []string{"deadbeef04"},
   400  				},
   401  			},
   402  		},
   403  	}
   404  }
   405  
   406  func getTestPaginationOracleData(t *testing.T, ctx context.Context, bs *sqlstore.Blocks, ds *sqlstore.OracleData) []entities.OracleData {
   407  	t.Helper()
   408  	protoData := getTestOracleData()
   409  	data := make([]entities.OracleData, 0, len(protoData))
   410  
   411  	blockTime := time.Now()
   412  
   413  	for i, item := range protoData {
   414  		block := addTestBlockForTime(t, ctx, bs, blockTime)
   415  		odEntity, err := entities.OracleDataFromProto(item, generateTxHash(), block.VegaTime, uint64(i))
   416  		require.NoError(t, err)
   417  
   418  		err = ds.Add(ctx, odEntity)
   419  		require.NoError(t, err)
   420  
   421  		data = append(data, *odEntity)
   422  		blockTime = blockTime.Add(time.Minute)
   423  	}
   424  
   425  	return data
   426  }
   427  
   428  func TestOracleData_GetOracleDataBySpecIDCursorPagination(t *testing.T) {
   429  	t.Run("should return all data when no pagination is provided", testOracleDataGetBySpecNoPagination)
   430  	t.Run("should return first page when first is provided", testOracleDataGetBySpecFirst)
   431  	t.Run("should return last page when last is provided", testOracleDataGetBySpecLast)
   432  	t.Run("should return requested page when first and after is provided", testOracleDataGetBySpecFirstAfter)
   433  	t.Run("should return requested page when last and before is provided", testOracleDataGetBySpecLastBefore)
   434  }
   435  
   436  func testOracleDataGetBySpecNoPagination(t *testing.T) {
   437  	ctx := tempTransaction(t)
   438  
   439  	bs, ds, _ := setupOracleDataTest(t)
   440  	data := getTestPaginationOracleData(t, ctx, bs, ds)
   441  
   442  	pagination, err := entities.NewCursorPagination(nil, nil, nil, nil, false)
   443  	require.NoError(t, err)
   444  
   445  	got, pageInfo, err := ds.ListOracleData(ctx, "deadbeef04", pagination)
   446  	require.NoError(t, err)
   447  	assert.Equal(t, data[4:], got)
   448  	assert.Equal(t, entities.PageInfo{
   449  		HasNextPage:     false,
   450  		HasPreviousPage: false,
   451  		StartCursor:     data[4].Cursor().Encode(),
   452  		EndCursor:       data[10].Cursor().Encode(),
   453  	}, pageInfo)
   454  }
   455  
   456  func testOracleDataGetBySpecFirst(t *testing.T) {
   457  	ctx := tempTransaction(t)
   458  
   459  	bs, ds, _ := setupOracleDataTest(t)
   460  	data := getTestPaginationOracleData(t, ctx, bs, ds)
   461  
   462  	first := int32(3)
   463  	pagination, err := entities.NewCursorPagination(&first, nil, nil, nil, false)
   464  	require.NoError(t, err)
   465  
   466  	got, pageInfo, err := ds.ListOracleData(ctx, "deadbeef04", pagination)
   467  	require.NoError(t, err)
   468  	assert.Equal(t, data[4:7], got)
   469  	assert.Equal(t, entities.PageInfo{
   470  		HasNextPage:     true,
   471  		HasPreviousPage: false,
   472  		StartCursor:     data[4].Cursor().Encode(),
   473  		EndCursor:       data[6].Cursor().Encode(),
   474  	}, pageInfo)
   475  }
   476  
   477  func testOracleDataGetBySpecLast(t *testing.T) {
   478  	ctx := tempTransaction(t)
   479  
   480  	bs, ds, _ := setupOracleDataTest(t)
   481  	data := getTestPaginationOracleData(t, ctx, bs, ds)
   482  
   483  	last := int32(3)
   484  	pagination, err := entities.NewCursorPagination(nil, nil, &last, nil, false)
   485  	require.NoError(t, err)
   486  
   487  	got, pageInfo, err := ds.ListOracleData(ctx, "deadbeef04", pagination)
   488  	require.NoError(t, err)
   489  	assert.Equal(t, data[8:], got)
   490  	assert.Equal(t, entities.PageInfo{
   491  		HasNextPage:     false,
   492  		HasPreviousPage: true,
   493  		StartCursor:     data[8].Cursor().Encode(),
   494  		EndCursor:       data[10].Cursor().Encode(),
   495  	}, pageInfo)
   496  }
   497  
   498  func testOracleDataGetBySpecFirstAfter(t *testing.T) {
   499  	ctx := tempTransaction(t)
   500  
   501  	bs, ds, _ := setupOracleDataTest(t)
   502  	data := getTestPaginationOracleData(t, ctx, bs, ds)
   503  
   504  	first := int32(3)
   505  	after := data[6].Cursor().Encode()
   506  	pagination, err := entities.NewCursorPagination(&first, &after, nil, nil, false)
   507  	require.NoError(t, err)
   508  
   509  	got, pageInfo, err := ds.ListOracleData(ctx, "deadbeef04", pagination)
   510  	require.NoError(t, err)
   511  	assert.Equal(t, data[7:10], got)
   512  	assert.Equal(t, entities.PageInfo{
   513  		HasNextPage:     true,
   514  		HasPreviousPage: true,
   515  		StartCursor:     data[7].Cursor().Encode(),
   516  		EndCursor:       data[9].Cursor().Encode(),
   517  	}, pageInfo)
   518  }
   519  
   520  func testOracleDataGetBySpecLastBefore(t *testing.T) {
   521  	ctx := tempTransaction(t)
   522  
   523  	bs, ds, _ := setupOracleDataTest(t)
   524  	data := getTestPaginationOracleData(t, ctx, bs, ds)
   525  
   526  	last := int32(3)
   527  	before := data[8].Cursor().Encode()
   528  	pagination, err := entities.NewCursorPagination(nil, nil, &last, &before, false)
   529  	require.NoError(t, err)
   530  
   531  	got, pageInfo, err := ds.ListOracleData(ctx, "deadbeef04", pagination)
   532  	require.NoError(t, err)
   533  	assert.Equal(t, data[5:8], got)
   534  	assert.Equal(t, entities.PageInfo{
   535  		HasNextPage:     true,
   536  		HasPreviousPage: true,
   537  		StartCursor:     data[5].Cursor().Encode(),
   538  		EndCursor:       data[7].Cursor().Encode(),
   539  	}, pageInfo)
   540  }
   541  
   542  // check when its empty what happens