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  }