github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/state/metadata_validator.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  	"time"
     8  
     9  	"github.com/MetalBlockchain/metalgo/codec"
    10  	"github.com/MetalBlockchain/metalgo/database"
    11  	"github.com/MetalBlockchain/metalgo/ids"
    12  	"github.com/MetalBlockchain/metalgo/utils/constants"
    13  	"github.com/MetalBlockchain/metalgo/utils/set"
    14  	"github.com/MetalBlockchain/metalgo/utils/wrappers"
    15  )
    16  
    17  // preDelegateeRewardSize is the size of codec marshalling
    18  // [preDelegateeRewardMetadata].
    19  //
    20  // CodecVersionLen + UpDurationLen + LastUpdatedLen + PotentialRewardLen
    21  const preDelegateeRewardSize = codec.VersionSize + 3*wrappers.LongLen
    22  
    23  var _ validatorState = (*metadata)(nil)
    24  
    25  type preDelegateeRewardMetadata struct {
    26  	UpDuration      time.Duration `v0:"true"`
    27  	LastUpdated     uint64        `v0:"true"` // Unix time in seconds
    28  	PotentialReward uint64        `v0:"true"`
    29  }
    30  
    31  type validatorMetadata struct {
    32  	UpDuration               time.Duration `v0:"true"`
    33  	LastUpdated              uint64        `v0:"true"` // Unix time in seconds
    34  	PotentialReward          uint64        `v0:"true"`
    35  	PotentialDelegateeReward uint64        `v0:"true"`
    36  	StakerStartTime          uint64        `          v1:"true"`
    37  
    38  	txID        ids.ID
    39  	lastUpdated time.Time
    40  }
    41  
    42  // Permissioned validators originally wrote their values as nil.
    43  // With Banff we wrote the potential reward.
    44  // With Cortina we wrote the potential reward with the potential delegatee reward.
    45  // We now write the uptime, reward, and delegatee reward together.
    46  func parseValidatorMetadata(bytes []byte, metadata *validatorMetadata) error {
    47  	switch len(bytes) {
    48  	case 0:
    49  	// nothing was stored
    50  
    51  	case database.Uint64Size:
    52  		// only potential reward was stored
    53  		var err error
    54  		metadata.PotentialReward, err = database.ParseUInt64(bytes)
    55  		if err != nil {
    56  			return err
    57  		}
    58  
    59  	case preDelegateeRewardSize:
    60  		// potential reward and uptime was stored but potential delegatee reward
    61  		// was not
    62  		tmp := preDelegateeRewardMetadata{}
    63  		if _, err := MetadataCodec.Unmarshal(bytes, &tmp); err != nil {
    64  			return err
    65  		}
    66  
    67  		metadata.UpDuration = tmp.UpDuration
    68  		metadata.LastUpdated = tmp.LastUpdated
    69  		metadata.PotentialReward = tmp.PotentialReward
    70  	default:
    71  		// everything was stored
    72  		if _, err := MetadataCodec.Unmarshal(bytes, metadata); err != nil {
    73  			return err
    74  		}
    75  	}
    76  	metadata.lastUpdated = time.Unix(int64(metadata.LastUpdated), 0)
    77  	return nil
    78  }
    79  
    80  type validatorState interface {
    81  	// LoadValidatorMetadata sets the [metadata] of [vdrID] on [subnetID].
    82  	// GetUptime and SetUptime will return an error if the [vdrID] and
    83  	// [subnetID] hasn't been loaded. This call will not result in a write to
    84  	// disk.
    85  	LoadValidatorMetadata(
    86  		vdrID ids.NodeID,
    87  		subnetID ids.ID,
    88  		metadata *validatorMetadata,
    89  	)
    90  
    91  	// GetUptime returns the current uptime measurements of [vdrID] on
    92  	// [subnetID].
    93  	GetUptime(
    94  		vdrID ids.NodeID,
    95  		subnetID ids.ID,
    96  	) (upDuration time.Duration, lastUpdated time.Time, err error)
    97  
    98  	// SetUptime updates the uptime measurements of [vdrID] on [subnetID].
    99  	// Unless these measurements are deleted first, the next call to
   100  	// WriteUptimes will write this update to disk.
   101  	SetUptime(
   102  		vdrID ids.NodeID,
   103  		subnetID ids.ID,
   104  		upDuration time.Duration,
   105  		lastUpdated time.Time,
   106  	) error
   107  
   108  	// GetDelegateeReward returns the current rewards accrued to [vdrID] on
   109  	// [subnetID].
   110  	GetDelegateeReward(
   111  		subnetID ids.ID,
   112  		vdrID ids.NodeID,
   113  	) (amount uint64, err error)
   114  
   115  	// SetDelegateeReward updates the rewards accrued to [vdrID] on [subnetID].
   116  	// Unless these measurements are deleted first, the next call to
   117  	// WriteUptimes will write this update to disk.
   118  	SetDelegateeReward(
   119  		subnetID ids.ID,
   120  		vdrID ids.NodeID,
   121  		amount uint64,
   122  	) error
   123  
   124  	// DeleteValidatorMetadata removes in-memory references to the metadata of
   125  	// [vdrID] on [subnetID]. If there were staged updates from a prior call to
   126  	// SetUptime or SetDelegateeReward, the updates will be dropped. This call
   127  	// will not result in a write to disk.
   128  	DeleteValidatorMetadata(vdrID ids.NodeID, subnetID ids.ID)
   129  
   130  	// WriteValidatorMetadata writes all staged updates from prior calls to
   131  	// SetUptime or SetDelegateeReward.
   132  	WriteValidatorMetadata(
   133  		dbPrimary database.KeyValueWriter,
   134  		dbSubnet database.KeyValueWriter,
   135  		codecVersion uint16,
   136  	) error
   137  }
   138  
   139  type metadata struct {
   140  	metadata map[ids.NodeID]map[ids.ID]*validatorMetadata // vdrID -> subnetID -> metadata
   141  	// updatedMetadata tracks the updates since WriteValidatorMetadata was last called
   142  	updatedMetadata map[ids.NodeID]set.Set[ids.ID] // vdrID -> subnetIDs
   143  }
   144  
   145  func newValidatorState() validatorState {
   146  	return &metadata{
   147  		metadata:        make(map[ids.NodeID]map[ids.ID]*validatorMetadata),
   148  		updatedMetadata: make(map[ids.NodeID]set.Set[ids.ID]),
   149  	}
   150  }
   151  
   152  func (m *metadata) LoadValidatorMetadata(
   153  	vdrID ids.NodeID,
   154  	subnetID ids.ID,
   155  	uptime *validatorMetadata,
   156  ) {
   157  	subnetMetadata, ok := m.metadata[vdrID]
   158  	if !ok {
   159  		subnetMetadata = make(map[ids.ID]*validatorMetadata)
   160  		m.metadata[vdrID] = subnetMetadata
   161  	}
   162  	subnetMetadata[subnetID] = uptime
   163  }
   164  
   165  func (m *metadata) GetUptime(
   166  	vdrID ids.NodeID,
   167  	subnetID ids.ID,
   168  ) (time.Duration, time.Time, error) {
   169  	metadata, exists := m.metadata[vdrID][subnetID]
   170  	if !exists {
   171  		return 0, time.Time{}, database.ErrNotFound
   172  	}
   173  	return metadata.UpDuration, metadata.lastUpdated, nil
   174  }
   175  
   176  func (m *metadata) SetUptime(
   177  	vdrID ids.NodeID,
   178  	subnetID ids.ID,
   179  	upDuration time.Duration,
   180  	lastUpdated time.Time,
   181  ) error {
   182  	metadata, exists := m.metadata[vdrID][subnetID]
   183  	if !exists {
   184  		return database.ErrNotFound
   185  	}
   186  	metadata.UpDuration = upDuration
   187  	metadata.lastUpdated = lastUpdated
   188  
   189  	m.addUpdatedMetadata(vdrID, subnetID)
   190  	return nil
   191  }
   192  
   193  func (m *metadata) GetDelegateeReward(
   194  	subnetID ids.ID,
   195  	vdrID ids.NodeID,
   196  ) (uint64, error) {
   197  	metadata, exists := m.metadata[vdrID][subnetID]
   198  	if !exists {
   199  		return 0, database.ErrNotFound
   200  	}
   201  	return metadata.PotentialDelegateeReward, nil
   202  }
   203  
   204  func (m *metadata) SetDelegateeReward(
   205  	subnetID ids.ID,
   206  	vdrID ids.NodeID,
   207  	amount uint64,
   208  ) error {
   209  	metadata, exists := m.metadata[vdrID][subnetID]
   210  	if !exists {
   211  		return database.ErrNotFound
   212  	}
   213  	metadata.PotentialDelegateeReward = amount
   214  
   215  	m.addUpdatedMetadata(vdrID, subnetID)
   216  	return nil
   217  }
   218  
   219  func (m *metadata) DeleteValidatorMetadata(vdrID ids.NodeID, subnetID ids.ID) {
   220  	subnetMetadata := m.metadata[vdrID]
   221  	delete(subnetMetadata, subnetID)
   222  	if len(subnetMetadata) == 0 {
   223  		delete(m.metadata, vdrID)
   224  	}
   225  
   226  	subnetUpdatedMetadata := m.updatedMetadata[vdrID]
   227  	subnetUpdatedMetadata.Remove(subnetID)
   228  	if subnetUpdatedMetadata.Len() == 0 {
   229  		delete(m.updatedMetadata, vdrID)
   230  	}
   231  }
   232  
   233  func (m *metadata) WriteValidatorMetadata(
   234  	dbPrimary database.KeyValueWriter,
   235  	dbSubnet database.KeyValueWriter,
   236  	codecVersion uint16,
   237  ) error {
   238  	for vdrID, updatedSubnets := range m.updatedMetadata {
   239  		for subnetID := range updatedSubnets {
   240  			metadata := m.metadata[vdrID][subnetID]
   241  			metadata.LastUpdated = uint64(metadata.lastUpdated.Unix())
   242  
   243  			metadataBytes, err := MetadataCodec.Marshal(codecVersion, metadata)
   244  			if err != nil {
   245  				return err
   246  			}
   247  			db := dbSubnet
   248  			if subnetID == constants.PrimaryNetworkID {
   249  				db = dbPrimary
   250  			}
   251  			if err := db.Put(metadata.txID[:], metadataBytes); err != nil {
   252  				return err
   253  			}
   254  		}
   255  		delete(m.updatedMetadata, vdrID)
   256  	}
   257  	return nil
   258  }
   259  
   260  func (m *metadata) addUpdatedMetadata(vdrID ids.NodeID, subnetID ids.ID) {
   261  	updatedSubnetMetadata, ok := m.updatedMetadata[vdrID]
   262  	if !ok {
   263  		updatedSubnetMetadata = set.Set[ids.ID]{}
   264  		m.updatedMetadata[vdrID] = updatedSubnetMetadata
   265  	}
   266  	updatedSubnetMetadata.Add(subnetID)
   267  }