github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/state/metadata_validator_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package state
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/codec"
    13  	"github.com/MetalBlockchain/metalgo/database"
    14  	"github.com/MetalBlockchain/metalgo/database/memdb"
    15  	"github.com/MetalBlockchain/metalgo/ids"
    16  	"github.com/MetalBlockchain/metalgo/utils/wrappers"
    17  )
    18  
    19  func TestValidatorUptimes(t *testing.T) {
    20  	require := require.New(t)
    21  	state := newValidatorState()
    22  
    23  	// get non-existent uptime
    24  	nodeID := ids.GenerateTestNodeID()
    25  	subnetID := ids.GenerateTestID()
    26  	_, _, err := state.GetUptime(nodeID, subnetID)
    27  	require.ErrorIs(err, database.ErrNotFound)
    28  
    29  	// set non-existent uptime
    30  	err = state.SetUptime(nodeID, subnetID, 1, time.Now())
    31  	require.ErrorIs(err, database.ErrNotFound)
    32  
    33  	testMetadata := &validatorMetadata{
    34  		UpDuration:  time.Hour,
    35  		lastUpdated: time.Now(),
    36  	}
    37  	// load uptime
    38  	state.LoadValidatorMetadata(nodeID, subnetID, testMetadata)
    39  
    40  	// get uptime
    41  	upDuration, lastUpdated, err := state.GetUptime(nodeID, subnetID)
    42  	require.NoError(err)
    43  	require.Equal(testMetadata.UpDuration, upDuration)
    44  	require.Equal(testMetadata.lastUpdated, lastUpdated)
    45  
    46  	// set uptime
    47  	newUpDuration := testMetadata.UpDuration + 1
    48  	newLastUpdated := testMetadata.lastUpdated.Add(time.Hour)
    49  	require.NoError(state.SetUptime(nodeID, subnetID, newUpDuration, newLastUpdated))
    50  
    51  	// get new uptime
    52  	upDuration, lastUpdated, err = state.GetUptime(nodeID, subnetID)
    53  	require.NoError(err)
    54  	require.Equal(newUpDuration, upDuration)
    55  	require.Equal(newLastUpdated, lastUpdated)
    56  
    57  	// load uptime changes uptimes
    58  	newTestMetadata := &validatorMetadata{
    59  		UpDuration:  testMetadata.UpDuration + time.Hour,
    60  		lastUpdated: testMetadata.lastUpdated.Add(time.Hour),
    61  	}
    62  	state.LoadValidatorMetadata(nodeID, subnetID, newTestMetadata)
    63  
    64  	// get new uptime
    65  	upDuration, lastUpdated, err = state.GetUptime(nodeID, subnetID)
    66  	require.NoError(err)
    67  	require.Equal(newTestMetadata.UpDuration, upDuration)
    68  	require.Equal(newTestMetadata.lastUpdated, lastUpdated)
    69  
    70  	// delete uptime
    71  	state.DeleteValidatorMetadata(nodeID, subnetID)
    72  
    73  	// get deleted uptime
    74  	_, _, err = state.GetUptime(nodeID, subnetID)
    75  	require.ErrorIs(err, database.ErrNotFound)
    76  }
    77  
    78  func TestWriteValidatorMetadata(t *testing.T) {
    79  	require := require.New(t)
    80  	state := newValidatorState()
    81  
    82  	primaryDB := memdb.New()
    83  	subnetDB := memdb.New()
    84  
    85  	// write empty uptimes
    86  	require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
    87  
    88  	// load uptime
    89  	nodeID := ids.GenerateTestNodeID()
    90  	subnetID := ids.GenerateTestID()
    91  	testUptimeReward := &validatorMetadata{
    92  		UpDuration:      time.Hour,
    93  		lastUpdated:     time.Now(),
    94  		PotentialReward: 100,
    95  		txID:            ids.GenerateTestID(),
    96  	}
    97  	state.LoadValidatorMetadata(nodeID, subnetID, testUptimeReward)
    98  
    99  	// write state, should not reflect to DB yet
   100  	require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
   101  	require.False(primaryDB.Has(testUptimeReward.txID[:]))
   102  	require.False(subnetDB.Has(testUptimeReward.txID[:]))
   103  
   104  	// get uptime should still return the loaded value
   105  	upDuration, lastUpdated, err := state.GetUptime(nodeID, subnetID)
   106  	require.NoError(err)
   107  	require.Equal(testUptimeReward.UpDuration, upDuration)
   108  	require.Equal(testUptimeReward.lastUpdated, lastUpdated)
   109  
   110  	// update uptimes
   111  	newUpDuration := testUptimeReward.UpDuration + 1
   112  	newLastUpdated := testUptimeReward.lastUpdated.Add(time.Hour)
   113  	require.NoError(state.SetUptime(nodeID, subnetID, newUpDuration, newLastUpdated))
   114  
   115  	// write uptimes, should reflect to subnet DB
   116  	require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
   117  	require.False(primaryDB.Has(testUptimeReward.txID[:]))
   118  	require.True(subnetDB.Has(testUptimeReward.txID[:]))
   119  }
   120  
   121  func TestValidatorDelegateeRewards(t *testing.T) {
   122  	require := require.New(t)
   123  	state := newValidatorState()
   124  
   125  	// get non-existent delegatee reward
   126  	nodeID := ids.GenerateTestNodeID()
   127  	subnetID := ids.GenerateTestID()
   128  	_, err := state.GetDelegateeReward(subnetID, nodeID)
   129  	require.ErrorIs(err, database.ErrNotFound)
   130  
   131  	// set non-existent delegatee reward
   132  	err = state.SetDelegateeReward(subnetID, nodeID, 100000)
   133  	require.ErrorIs(err, database.ErrNotFound)
   134  
   135  	testMetadata := &validatorMetadata{
   136  		PotentialDelegateeReward: 100000,
   137  	}
   138  	// load delegatee reward
   139  	state.LoadValidatorMetadata(nodeID, subnetID, testMetadata)
   140  
   141  	// get delegatee reward
   142  	delegateeReward, err := state.GetDelegateeReward(subnetID, nodeID)
   143  	require.NoError(err)
   144  	require.Equal(testMetadata.PotentialDelegateeReward, delegateeReward)
   145  
   146  	// set delegatee reward
   147  	newDelegateeReward := testMetadata.PotentialDelegateeReward + 100000
   148  	require.NoError(state.SetDelegateeReward(subnetID, nodeID, newDelegateeReward))
   149  
   150  	// get new delegatee reward
   151  	delegateeReward, err = state.GetDelegateeReward(subnetID, nodeID)
   152  	require.NoError(err)
   153  	require.Equal(newDelegateeReward, delegateeReward)
   154  
   155  	// load delegatee reward changes
   156  	newTestMetadata := &validatorMetadata{
   157  		PotentialDelegateeReward: testMetadata.PotentialDelegateeReward + 100000,
   158  	}
   159  	state.LoadValidatorMetadata(nodeID, subnetID, newTestMetadata)
   160  
   161  	// get new delegatee reward
   162  	delegateeReward, err = state.GetDelegateeReward(subnetID, nodeID)
   163  	require.NoError(err)
   164  	require.Equal(newTestMetadata.PotentialDelegateeReward, delegateeReward)
   165  
   166  	// delete delegatee reward
   167  	state.DeleteValidatorMetadata(nodeID, subnetID)
   168  
   169  	// get deleted delegatee reward
   170  	_, _, err = state.GetUptime(nodeID, subnetID)
   171  	require.ErrorIs(err, database.ErrNotFound)
   172  }
   173  
   174  func TestParseValidatorMetadata(t *testing.T) {
   175  	type test struct {
   176  		name        string
   177  		bytes       []byte
   178  		expected    *validatorMetadata
   179  		expectedErr error
   180  	}
   181  	tests := []test{
   182  		{
   183  			name:  "nil",
   184  			bytes: nil,
   185  			expected: &validatorMetadata{
   186  				lastUpdated: time.Unix(0, 0),
   187  			},
   188  			expectedErr: nil,
   189  		},
   190  		{
   191  			name:  "nil",
   192  			bytes: []byte{},
   193  			expected: &validatorMetadata{
   194  				lastUpdated: time.Unix(0, 0),
   195  			},
   196  			expectedErr: nil,
   197  		},
   198  		{
   199  			name: "potential reward only",
   200  			bytes: []byte{
   201  				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0,
   202  			},
   203  			expected: &validatorMetadata{
   204  				PotentialReward: 100000,
   205  				lastUpdated:     time.Unix(0, 0),
   206  			},
   207  			expectedErr: nil,
   208  		},
   209  		{
   210  			name: "uptime + potential reward",
   211  			bytes: []byte{
   212  				// codec version
   213  				0x00, 0x00,
   214  				// up duration
   215  				0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80,
   216  				// last updated
   217  				0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xBB, 0xA0,
   218  				// potential reward
   219  				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0,
   220  			},
   221  			expected: &validatorMetadata{
   222  				UpDuration:      6000000,
   223  				LastUpdated:     900000,
   224  				PotentialReward: 100000,
   225  				lastUpdated:     time.Unix(900000, 0),
   226  			},
   227  			expectedErr: nil,
   228  		},
   229  		{
   230  			name: "uptime + potential reward + potential delegatee reward",
   231  			bytes: []byte{
   232  				// codec version
   233  				0x00, 0x00,
   234  				// up duration
   235  				0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80,
   236  				// last updated
   237  				0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xBB, 0xA0,
   238  				// potential reward
   239  				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0,
   240  				// potential delegatee reward
   241  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x20,
   242  			},
   243  			expected: &validatorMetadata{
   244  				UpDuration:               6000000,
   245  				LastUpdated:              900000,
   246  				PotentialReward:          100000,
   247  				PotentialDelegateeReward: 20000,
   248  				lastUpdated:              time.Unix(900000, 0),
   249  			},
   250  			expectedErr: nil,
   251  		},
   252  		{
   253  			name: "invalid codec version",
   254  			bytes: []byte{
   255  				// codec version
   256  				0x00, 0x02,
   257  				// up duration
   258  				0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80,
   259  				// last updated
   260  				0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xBB, 0xA0,
   261  				// potential reward
   262  				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0,
   263  				// potential delegatee reward
   264  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x20,
   265  			},
   266  			expected:    nil,
   267  			expectedErr: codec.ErrUnknownVersion,
   268  		},
   269  		{
   270  			name: "short byte len",
   271  			bytes: []byte{
   272  				// codec version
   273  				0x00, 0x00,
   274  				// up duration
   275  				0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80,
   276  				// last updated
   277  				0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xBB, 0xA0,
   278  				// potential reward
   279  				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0,
   280  				// potential delegatee reward
   281  				0x00, 0x00, 0x00, 0x00, 0x4E, 0x20,
   282  			},
   283  			expected:    nil,
   284  			expectedErr: wrappers.ErrInsufficientLength,
   285  		},
   286  	}
   287  	for _, tt := range tests {
   288  		t.Run(tt.name, func(t *testing.T) {
   289  			require := require.New(t)
   290  			var metadata validatorMetadata
   291  			err := parseValidatorMetadata(tt.bytes, &metadata)
   292  			require.ErrorIs(err, tt.expectedErr)
   293  			if tt.expectedErr != nil {
   294  				return
   295  			}
   296  			require.Equal(tt.expected, &metadata)
   297  		})
   298  	}
   299  }