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 }