github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/identity.go (about) 1 package flow 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 8 "github.com/ethereum/go-ethereum/rlp" 9 "github.com/fxamacker/cbor/v2" 10 "github.com/onflow/crypto" 11 "github.com/vmihailenco/msgpack" 12 ) 13 14 // DefaultInitialWeight is the default initial weight for a node identity. 15 // It is equal to the default initial weight in the FlowIDTableStaking smart contract. 16 const DefaultInitialWeight = 100 17 18 // IdentitySkeleton represents the static part of a network participant's (i.e. node's) public identity. 19 type IdentitySkeleton struct { 20 // NodeID uniquely identifies a particular node. A node's ID is fixed for 21 // the duration of that node's participation in the network. 22 NodeID Identifier 23 // Address is the network address where the node can be reached. 24 Address string 25 // Role is the node's role in the network and defines its abilities and 26 // responsibilities. 27 Role Role 28 // InitialWeight is a 'trust score' initially assigned by EpochSetup event after 29 // the staking phase. The initial weights define the supermajority thresholds for 30 // the cluster and security node consensus throughout the Epoch. 31 InitialWeight uint64 32 StakingPubKey crypto.PublicKey 33 NetworkPubKey crypto.PublicKey 34 } 35 36 // EpochParticipationStatus represents the status of a node's participation. Depending on what 37 // changes were applied to the protocol state, a node may be in one of four states: 38 // / - joining - the node is not active in the current epoch and will be active in the next epoch. 39 // / - active - the node was included in the EpochSetup event for the current epoch and is actively participating in the current epoch. 40 // / - leaving - the node was active in the previous epoch but is not active in the current epoch. 41 // / - ejected - the node has been permanently removed from the network. 42 // 43 // / EpochSetup 44 // / ┌────────────⬤ unregistered ◯◄───────────┐ 45 // / ┌─────▼─────┐ ┌───────────┐ ┌─────┴─────┐ 46 // / │ JOINING ├───────►│ ACTIVE ├───────►│ LEAVING │ 47 // / └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ 48 // / │ ┌─────▼─────┐ │ 49 // / └─────────────►│ EJECTED │◄─────────────┘ 50 // / └───────────┘ 51 // 52 // Only active nodes are allowed to perform certain tasks relative to other nodes. 53 // Nodes which are registered to join at the next epoch will appear in the 54 // identity table but aren't considered active until their first 55 // epoch begins. Likewise, nodes which were registered in the previous epoch 56 // but have left at the most recent epoch boundary will appear in the identity 57 // table with leaving participation status. 58 // A node may be ejected by either: 59 // - requesting self-ejection to protect its stake in case the node operator suspects 60 // the node's keys to be compromised 61 // - committing a serious protocol violation or multiple smaller misdemeanours. 62 type EpochParticipationStatus int 63 64 const ( 65 EpochParticipationStatusJoining EpochParticipationStatus = iota 66 EpochParticipationStatusActive 67 EpochParticipationStatusLeaving 68 EpochParticipationStatusEjected 69 ) 70 71 // String returns string representation of enum value. 72 func (s EpochParticipationStatus) String() string { 73 return [...]string{ 74 "EpochParticipationStatusJoining", 75 "EpochParticipationStatusActive", 76 "EpochParticipationStatusLeaving", 77 "EpochParticipationStatusEjected", 78 }[s] 79 } 80 81 // ParseEpochParticipationStatus converts string representation of EpochParticipationStatus into a typed value. 82 // An exception will be returned if failed to convert. 83 func ParseEpochParticipationStatus(s string) (EpochParticipationStatus, error) { 84 switch s { 85 case EpochParticipationStatusJoining.String(): 86 return EpochParticipationStatusJoining, nil 87 case EpochParticipationStatusActive.String(): 88 return EpochParticipationStatusActive, nil 89 case EpochParticipationStatusLeaving.String(): 90 return EpochParticipationStatusLeaving, nil 91 case EpochParticipationStatusEjected.String(): 92 return EpochParticipationStatusEjected, nil 93 default: 94 return 0, fmt.Errorf("invalid epoch participation status") 95 } 96 } 97 98 // EncodeRLP performs RLP encoding of custom type, it's need to be able to hash structures that include EpochParticipationStatus. 99 // No errors are expected during normal operations. 100 func (s EpochParticipationStatus) EncodeRLP(w io.Writer) error { 101 encodable := s.String() 102 err := rlp.Encode(w, encodable) 103 if err != nil { 104 return fmt.Errorf("could not encode rlp: %w", err) 105 } 106 return nil 107 } 108 109 // DynamicIdentity represents the dynamic part of public identity of one network participant (node). 110 type DynamicIdentity struct { 111 EpochParticipationStatus 112 } 113 114 // Identity is combined from static and dynamic part and represents the full public identity of one network participant (node). 115 type Identity struct { 116 IdentitySkeleton 117 DynamicIdentity 118 } 119 120 // IsEjected returns true if the node is ejected from the network. 121 func (iy *DynamicIdentity) IsEjected() bool { 122 return iy.EpochParticipationStatus == EpochParticipationStatusEjected 123 } 124 125 // String returns a string representation of the identity. 126 func (iy Identity) String() string { 127 return fmt.Sprintf("%s-%s@%s=%s", iy.Role, iy.NodeID.String(), iy.Address, iy.EpochParticipationStatus.String()) 128 } 129 130 // String returns a string representation of the identity. 131 func (iy IdentitySkeleton) String() string { 132 return fmt.Sprintf("%s-%s@%s", iy.Role, iy.NodeID.String(), iy.Address) 133 } 134 135 // ID returns a unique, persistent identifier for the identity. 136 // CAUTION: the ID may be chosen by a node operator, so long as it is unique. 137 func (iy Identity) ID() Identifier { 138 return iy.NodeID 139 } 140 141 // Checksum returns a checksum for the identity including mutable attributes. 142 func (iy Identity) Checksum() Identifier { 143 return MakeID(iy) 144 } 145 146 // GetNodeID returns node ID for the identity. It is needed to satisfy GenericIdentity constraint. 147 func (iy IdentitySkeleton) GetNodeID() Identifier { 148 return iy.NodeID 149 } 150 151 // GetRole returns a node role for the identity. It is needed to satisfy GenericIdentity constraint. 152 func (iy IdentitySkeleton) GetRole() Role { 153 return iy.Role 154 } 155 156 // GetStakingPubKey returns staking public key for the identity. It is needed to satisfy GenericIdentity constraint. 157 func (iy IdentitySkeleton) GetStakingPubKey() crypto.PublicKey { 158 return iy.StakingPubKey 159 } 160 161 // GetNetworkPubKey returns network public key for the identity. It is needed to satisfy GenericIdentity constraint. 162 func (iy IdentitySkeleton) GetNetworkPubKey() crypto.PublicKey { 163 return iy.NetworkPubKey 164 } 165 166 // GetInitialWeight returns initial weight for the identity. It is needed to satisfy GenericIdentity constraint. 167 func (iy IdentitySkeleton) GetInitialWeight() uint64 { 168 return iy.InitialWeight 169 } 170 171 // GetSkeleton returns the skeleton part for the identity. It is needed to satisfy GenericIdentity constraint. 172 func (iy IdentitySkeleton) GetSkeleton() IdentitySkeleton { 173 return iy 174 } 175 176 type encodableIdentitySkeleton struct { 177 NodeID Identifier 178 Address string `json:",omitempty"` 179 Role Role 180 InitialWeight uint64 181 StakingPubKey []byte 182 NetworkPubKey []byte 183 } 184 185 type encodableIdentity struct { 186 encodableIdentitySkeleton 187 ParticipationStatus string 188 } 189 190 func encodableSkeletonFromIdentity(iy IdentitySkeleton) encodableIdentitySkeleton { 191 ie := encodableIdentitySkeleton{ 192 NodeID: iy.NodeID, 193 Address: iy.Address, 194 Role: iy.Role, 195 InitialWeight: iy.InitialWeight, 196 } 197 if iy.StakingPubKey != nil { 198 ie.StakingPubKey = iy.StakingPubKey.Encode() 199 } 200 if iy.NetworkPubKey != nil { 201 ie.NetworkPubKey = iy.NetworkPubKey.Encode() 202 } 203 return ie 204 } 205 206 func encodableFromIdentity(iy Identity) encodableIdentity { 207 return encodableIdentity{ 208 encodableIdentitySkeleton: encodableSkeletonFromIdentity(iy.IdentitySkeleton), 209 ParticipationStatus: iy.EpochParticipationStatus.String(), 210 } 211 } 212 213 func (iy IdentitySkeleton) MarshalJSON() ([]byte, error) { 214 encodable := encodableSkeletonFromIdentity(iy) 215 data, err := json.Marshal(encodable) 216 if err != nil { 217 return nil, fmt.Errorf("could not encode json: %w", err) 218 } 219 return data, nil 220 } 221 222 func (iy IdentitySkeleton) MarshalCBOR() ([]byte, error) { 223 encodable := encodableSkeletonFromIdentity(iy) 224 data, err := cbor.Marshal(encodable) 225 if err != nil { 226 return nil, fmt.Errorf("could not encode cbor: %w", err) 227 } 228 return data, nil 229 } 230 231 func (iy IdentitySkeleton) MarshalMsgpack() ([]byte, error) { 232 encodable := encodableSkeletonFromIdentity(iy) 233 data, err := msgpack.Marshal(encodable) 234 if err != nil { 235 return nil, fmt.Errorf("could not encode msgpack: %w", err) 236 } 237 return data, nil 238 } 239 240 func (iy IdentitySkeleton) EncodeRLP(w io.Writer) error { 241 encodable := encodableSkeletonFromIdentity(iy) 242 err := rlp.Encode(w, encodable) 243 if err != nil { 244 return fmt.Errorf("could not encode rlp: %w", err) 245 } 246 return nil 247 } 248 249 func (iy Identity) MarshalJSON() ([]byte, error) { 250 encodable := encodableFromIdentity(iy) 251 data, err := json.Marshal(encodable) 252 if err != nil { 253 return nil, fmt.Errorf("could not encode json: %w", err) 254 } 255 return data, nil 256 } 257 258 func (iy Identity) MarshalCBOR() ([]byte, error) { 259 encodable := encodableFromIdentity(iy) 260 data, err := cbor.Marshal(encodable) 261 if err != nil { 262 return nil, fmt.Errorf("could not encode cbor: %w", err) 263 } 264 return data, nil 265 } 266 267 func (iy Identity) MarshalMsgpack() ([]byte, error) { 268 encodable := encodableFromIdentity(iy) 269 data, err := msgpack.Marshal(encodable) 270 if err != nil { 271 return nil, fmt.Errorf("could not encode msgpack: %w", err) 272 } 273 return data, nil 274 } 275 276 func (iy Identity) EncodeRLP(w io.Writer) error { 277 encodable := encodableFromIdentity(iy) 278 err := rlp.Encode(w, encodable) 279 if err != nil { 280 return fmt.Errorf("could not encode rlp: %w", err) 281 } 282 return nil 283 } 284 285 func identitySkeletonFromEncodable(ie encodableIdentitySkeleton, identity *IdentitySkeleton) error { 286 identity.NodeID = ie.NodeID 287 identity.Address = ie.Address 288 identity.Role = ie.Role 289 identity.InitialWeight = ie.InitialWeight 290 var err error 291 if ie.StakingPubKey != nil { 292 if identity.StakingPubKey, err = crypto.DecodePublicKey(crypto.BLSBLS12381, ie.StakingPubKey); err != nil { 293 return fmt.Errorf("could not decode staking key: %w", err) 294 } 295 } 296 if ie.NetworkPubKey != nil { 297 if identity.NetworkPubKey, err = crypto.DecodePublicKey(crypto.ECDSAP256, ie.NetworkPubKey); err != nil { 298 return fmt.Errorf("could not decode network key: %w", err) 299 } 300 } 301 return nil 302 } 303 304 func identityFromEncodable(ie encodableIdentity, identity *Identity) error { 305 err := identitySkeletonFromEncodable(ie.encodableIdentitySkeleton, &identity.IdentitySkeleton) 306 if err != nil { 307 return fmt.Errorf("could not decode identity skeleton: %w", err) 308 } 309 participationStatus, err := ParseEpochParticipationStatus(ie.ParticipationStatus) 310 if err != nil { 311 return fmt.Errorf("could not decode epoch participation status: %w", err) 312 } 313 identity.EpochParticipationStatus = participationStatus 314 return nil 315 } 316 317 func (iy *IdentitySkeleton) UnmarshalJSON(b []byte) error { 318 var decodable encodableIdentitySkeleton 319 err := json.Unmarshal(b, &decodable) 320 if err != nil { 321 return fmt.Errorf("could not decode json: %w", err) 322 } 323 err = identitySkeletonFromEncodable(decodable, iy) 324 if err != nil { 325 return fmt.Errorf("could not convert from encodable json: %w", err) 326 } 327 return nil 328 } 329 330 func (iy *IdentitySkeleton) UnmarshalCBOR(b []byte) error { 331 var encodable encodableIdentitySkeleton 332 err := cbor.Unmarshal(b, &encodable) 333 if err != nil { 334 return fmt.Errorf("could not decode json: %w", err) 335 } 336 err = identitySkeletonFromEncodable(encodable, iy) 337 if err != nil { 338 return fmt.Errorf("could not convert from encodable cbor: %w", err) 339 } 340 return nil 341 } 342 343 func (iy *IdentitySkeleton) UnmarshalMsgpack(b []byte) error { 344 var encodable encodableIdentitySkeleton 345 err := msgpack.Unmarshal(b, &encodable) 346 if err != nil { 347 return fmt.Errorf("could not decode json: %w", err) 348 } 349 err = identitySkeletonFromEncodable(encodable, iy) 350 if err != nil { 351 return fmt.Errorf("could not convert from encodable msgpack: %w", err) 352 } 353 return nil 354 } 355 356 func (iy *Identity) UnmarshalJSON(b []byte) error { 357 var decodable encodableIdentity 358 err := json.Unmarshal(b, &decodable) 359 if err != nil { 360 return fmt.Errorf("could not decode json: %w", err) 361 } 362 err = identityFromEncodable(decodable, iy) 363 if err != nil { 364 return fmt.Errorf("could not convert from encodable json: %w", err) 365 } 366 return nil 367 } 368 369 func (iy *Identity) UnmarshalCBOR(b []byte) error { 370 var encodable encodableIdentity 371 err := cbor.Unmarshal(b, &encodable) 372 if err != nil { 373 return fmt.Errorf("could not decode json: %w", err) 374 } 375 err = identityFromEncodable(encodable, iy) 376 if err != nil { 377 return fmt.Errorf("could not convert from encodable cbor: %w", err) 378 } 379 return nil 380 } 381 382 func (iy *Identity) UnmarshalMsgpack(b []byte) error { 383 var encodable encodableIdentity 384 err := msgpack.Unmarshal(b, &encodable) 385 if err != nil { 386 return fmt.Errorf("could not decode json: %w", err) 387 } 388 err = identityFromEncodable(encodable, iy) 389 if err != nil { 390 return fmt.Errorf("could not convert from encodable msgpack: %w", err) 391 } 392 return nil 393 } 394 395 func (iy *IdentitySkeleton) EqualTo(other *IdentitySkeleton) bool { 396 if iy.NodeID != other.NodeID { 397 return false 398 } 399 if iy.Address != other.Address { 400 return false 401 } 402 if iy.Role != other.Role { 403 return false 404 } 405 if iy.InitialWeight != other.InitialWeight { 406 return false 407 } 408 if (iy.StakingPubKey != nil && other.StakingPubKey == nil) || 409 (iy.StakingPubKey == nil && other.StakingPubKey != nil) { 410 return false 411 } 412 if iy.StakingPubKey != nil && !iy.StakingPubKey.Equals(other.StakingPubKey) { 413 return false 414 } 415 416 if (iy.NetworkPubKey != nil && other.NetworkPubKey == nil) || 417 (iy.NetworkPubKey == nil && other.NetworkPubKey != nil) { 418 return false 419 } 420 if iy.NetworkPubKey != nil && !iy.NetworkPubKey.Equals(other.NetworkPubKey) { 421 return false 422 } 423 424 return true 425 } 426 427 func (iy *DynamicIdentity) EqualTo(other *DynamicIdentity) bool { 428 return iy.EpochParticipationStatus == other.EpochParticipationStatus 429 } 430 431 func (iy *Identity) EqualTo(other *Identity) bool { 432 if !iy.IdentitySkeleton.EqualTo(&other.IdentitySkeleton) { 433 return false 434 } 435 if !iy.DynamicIdentity.EqualTo(&other.DynamicIdentity) { 436 return false 437 } 438 return true 439 }