github.com/onflow/flow-go@v0.33.17/model/flow/identity.go (about)

     1  package flow
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"regexp"
     9  	"strconv"
    10  
    11  	"golang.org/x/exp/slices"
    12  
    13  	"github.com/ethereum/go-ethereum/rlp"
    14  	"github.com/fxamacker/cbor/v2"
    15  	"github.com/pkg/errors"
    16  	"github.com/vmihailenco/msgpack"
    17  
    18  	"github.com/onflow/flow-go/crypto"
    19  	"github.com/onflow/flow-go/utils/rand"
    20  )
    21  
    22  // DefaultInitialWeight is the default initial weight for a node identity.
    23  // It is equal to the default initial weight in the FlowIDTableStaking smart contract.
    24  const DefaultInitialWeight = 100
    25  
    26  // rxid is the regex for parsing node identity entries.
    27  var rxid = regexp.MustCompile(`^(collection|consensus|execution|verification|access)-([0-9a-fA-F]{64})@([\w\d]+|[\w\d][\w\d\-]*[\w\d](?:\.*[\w\d][\w\d\-]*[\w\d])*|[\w\d][\w\d\-]*[\w\d])(:[\d]+)?=(\d{1,20})$`)
    28  
    29  // Identity represents the public identity of one network participant (node).
    30  type Identity struct {
    31  	// NodeID uniquely identifies a particular node. A node's ID is fixed for
    32  	// the duration of that node's participation in the network.
    33  	NodeID Identifier
    34  	// Address is the network address where the node can be reached.
    35  	Address string
    36  	// Role is the node's role in the network and defines its abilities and
    37  	// responsibilities.
    38  	Role Role
    39  	// Weight represents the node's authority to perform certain tasks relative
    40  	// to other nodes. For example, in the consensus committee, the node's weight
    41  	// represents the weight assigned to its votes.
    42  	//
    43  	// A node's weight is distinct from its stake. Stake represents the quantity
    44  	// of FLOW tokens held by the network in escrow during the course of the node's
    45  	// participation in the network. The stake is strictly managed by the service
    46  	// account smart contracts.
    47  	//
    48  	// Nodes which are registered to join at the next epoch will appear in the
    49  	// identity table but are considered to have zero weight up until their first
    50  	// epoch begins. Likewise nodes which were registered in the previous epoch
    51  	// but have left at the most recent epoch boundary will appear in the identity
    52  	// table with zero weight.
    53  	Weight uint64
    54  	// Ejected represents whether a node has been permanently removed from the
    55  	// network. A node may be ejected for either:
    56  	// * committing one protocol felony
    57  	// * committing a series of protocol misdemeanours
    58  	Ejected       bool
    59  	StakingPubKey crypto.PublicKey
    60  	NetworkPubKey crypto.PublicKey
    61  }
    62  
    63  func (id *Identity) Equals(other *Identity) bool {
    64  	if other == nil {
    65  		return false
    66  	}
    67  	return id.NodeID == other.NodeID &&
    68  		id.Address == other.Address &&
    69  		id.Role == other.Role &&
    70  		id.Weight == other.Weight &&
    71  		id.Ejected == other.Ejected &&
    72  		id.StakingPubKey.Equals(other.StakingPubKey) &&
    73  		id.NetworkPubKey.Equals(other.NetworkPubKey)
    74  }
    75  
    76  // ParseIdentity parses a string representation of an identity.
    77  func ParseIdentity(identity string) (*Identity, error) {
    78  
    79  	// use the regex to match the four parts of an identity
    80  	matches := rxid.FindStringSubmatch(identity)
    81  	if len(matches) != 6 {
    82  		return nil, errors.New("invalid identity string format")
    83  	}
    84  
    85  	// none of these will error as they are checked by the regex
    86  	var nodeID Identifier
    87  	nodeID, err := HexStringToIdentifier(matches[2])
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	address := matches[3] + matches[4]
    92  	role, _ := ParseRole(matches[1])
    93  	weight, _ := strconv.ParseUint(matches[5], 10, 64)
    94  
    95  	// create the identity
    96  	iy := Identity{
    97  		NodeID:  nodeID,
    98  		Address: address,
    99  		Role:    role,
   100  		Weight:  weight,
   101  	}
   102  
   103  	return &iy, nil
   104  }
   105  
   106  // String returns a string representation of the identity.
   107  func (iy Identity) String() string {
   108  	return fmt.Sprintf("%s-%s@%s=%d", iy.Role, iy.NodeID.String(), iy.Address, iy.Weight)
   109  }
   110  
   111  // ID returns a unique, persistent identifier for the identity.
   112  // CAUTION: the ID may be chosen by a node operator, so long as it is unique.
   113  func (iy Identity) ID() Identifier {
   114  	return iy.NodeID
   115  }
   116  
   117  // Checksum returns a checksum for the identity including mutable attributes.
   118  func (iy Identity) Checksum() Identifier {
   119  	return MakeID(iy)
   120  }
   121  
   122  type encodableIdentity struct {
   123  	NodeID        Identifier
   124  	Address       string `json:",omitempty"`
   125  	Role          Role
   126  	Weight        uint64
   127  	StakingPubKey []byte
   128  	NetworkPubKey []byte
   129  }
   130  
   131  // decodableIdentity provides backward-compatible decoding of old models
   132  // which use the Stake field in place of Weight.
   133  type decodableIdentity struct {
   134  	encodableIdentity
   135  	// Stake previously was used in place of the Weight field.
   136  	// Deprecated: supported in decoding for backward-compatibility
   137  	Stake uint64
   138  }
   139  
   140  func encodableFromIdentity(iy Identity) (encodableIdentity, error) {
   141  	ie := encodableIdentity{iy.NodeID, iy.Address, iy.Role, iy.Weight, nil, nil}
   142  	if iy.StakingPubKey != nil {
   143  		ie.StakingPubKey = iy.StakingPubKey.Encode()
   144  	}
   145  	if iy.NetworkPubKey != nil {
   146  		ie.NetworkPubKey = iy.NetworkPubKey.Encode()
   147  	}
   148  	return ie, nil
   149  }
   150  
   151  func (iy Identity) MarshalJSON() ([]byte, error) {
   152  	encodable, err := encodableFromIdentity(iy)
   153  	if err != nil {
   154  		return nil, fmt.Errorf("could not convert identity to encodable: %w", err)
   155  	}
   156  
   157  	data, err := json.Marshal(encodable)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("could not encode json: %w", err)
   160  	}
   161  	return data, nil
   162  }
   163  
   164  func (iy Identity) MarshalCBOR() ([]byte, error) {
   165  	encodable, err := encodableFromIdentity(iy)
   166  	if err != nil {
   167  		return nil, fmt.Errorf("could not convert identity to encodable: %w", err)
   168  	}
   169  	data, err := cbor.Marshal(encodable)
   170  	if err != nil {
   171  		return nil, fmt.Errorf("could not encode cbor: %w", err)
   172  	}
   173  	return data, nil
   174  }
   175  
   176  func (iy Identity) MarshalMsgpack() ([]byte, error) {
   177  	encodable, err := encodableFromIdentity(iy)
   178  	if err != nil {
   179  		return nil, fmt.Errorf("could not convert to encodable: %w", err)
   180  	}
   181  	data, err := msgpack.Marshal(encodable)
   182  	if err != nil {
   183  		return nil, fmt.Errorf("could not encode msgpack: %w", err)
   184  	}
   185  	return data, nil
   186  }
   187  
   188  func (iy Identity) EncodeRLP(w io.Writer) error {
   189  	encodable, err := encodableFromIdentity(iy)
   190  	if err != nil {
   191  		return fmt.Errorf("could not convert to encodable: %w", err)
   192  	}
   193  	err = rlp.Encode(w, encodable)
   194  	if err != nil {
   195  		return fmt.Errorf("could not encode rlp: %w", err)
   196  	}
   197  	return nil
   198  }
   199  
   200  func identityFromEncodable(ie encodableIdentity, identity *Identity) error {
   201  	identity.NodeID = ie.NodeID
   202  	identity.Address = ie.Address
   203  	identity.Role = ie.Role
   204  	identity.Weight = ie.Weight
   205  	var err error
   206  	if ie.StakingPubKey != nil {
   207  		if identity.StakingPubKey, err = crypto.DecodePublicKey(crypto.BLSBLS12381, ie.StakingPubKey); err != nil {
   208  			return fmt.Errorf("could not decode staking key: %w", err)
   209  		}
   210  	}
   211  	if ie.NetworkPubKey != nil {
   212  		if identity.NetworkPubKey, err = crypto.DecodePublicKey(crypto.ECDSAP256, ie.NetworkPubKey); err != nil {
   213  			return fmt.Errorf("could not decode network key: %w", err)
   214  		}
   215  	}
   216  	return nil
   217  }
   218  
   219  func (iy *Identity) UnmarshalJSON(b []byte) error {
   220  	var decodable decodableIdentity
   221  	err := json.Unmarshal(b, &decodable)
   222  	if err != nil {
   223  		return fmt.Errorf("could not decode json: %w", err)
   224  	}
   225  	// compat: translate Stake fields to Weight
   226  	if decodable.Stake != 0 {
   227  		if decodable.Weight != 0 {
   228  			return fmt.Errorf("invalid identity with both Stake and Weight fields")
   229  		}
   230  		decodable.Weight = decodable.Stake
   231  	}
   232  	err = identityFromEncodable(decodable.encodableIdentity, iy)
   233  	if err != nil {
   234  		return fmt.Errorf("could not convert from encodable json: %w", err)
   235  	}
   236  	return nil
   237  }
   238  
   239  func (iy *Identity) UnmarshalCBOR(b []byte) error {
   240  	var encodable encodableIdentity
   241  	err := cbor.Unmarshal(b, &encodable)
   242  	if err != nil {
   243  		return fmt.Errorf("could not decode json: %w", err)
   244  	}
   245  	err = identityFromEncodable(encodable, iy)
   246  	if err != nil {
   247  		return fmt.Errorf("could not convert from encodable cbor: %w", err)
   248  	}
   249  	return nil
   250  }
   251  
   252  func (iy *Identity) UnmarshalMsgpack(b []byte) error {
   253  	var encodable encodableIdentity
   254  	err := msgpack.Unmarshal(b, &encodable)
   255  	if err != nil {
   256  		return fmt.Errorf("could not decode json: %w", err)
   257  	}
   258  	err = identityFromEncodable(encodable, iy)
   259  	if err != nil {
   260  		return fmt.Errorf("could not convert from encodable msgpack: %w", err)
   261  	}
   262  	return nil
   263  }
   264  
   265  func (iy *Identity) EqualTo(other *Identity) bool {
   266  	if iy.NodeID != other.NodeID {
   267  		return false
   268  	}
   269  	if iy.Address != other.Address {
   270  		return false
   271  	}
   272  	if iy.Role != other.Role {
   273  		return false
   274  	}
   275  	if iy.Weight != other.Weight {
   276  		return false
   277  	}
   278  	if iy.Ejected != other.Ejected {
   279  		return false
   280  	}
   281  	if (iy.StakingPubKey != nil && other.StakingPubKey == nil) ||
   282  		(iy.StakingPubKey == nil && other.StakingPubKey != nil) {
   283  		return false
   284  	}
   285  	if iy.StakingPubKey != nil && !iy.StakingPubKey.Equals(other.StakingPubKey) {
   286  		return false
   287  	}
   288  
   289  	if (iy.NetworkPubKey != nil && other.NetworkPubKey == nil) ||
   290  		(iy.NetworkPubKey == nil && other.NetworkPubKey != nil) {
   291  		return false
   292  	}
   293  	if iy.NetworkPubKey != nil && !iy.NetworkPubKey.Equals(other.NetworkPubKey) {
   294  		return false
   295  	}
   296  
   297  	return true
   298  }
   299  
   300  // IdentityFilter is a filter on identities.
   301  type IdentityFilter func(*Identity) bool
   302  
   303  // IdentityOrder is an order function for identities.
   304  //
   305  // It defines a strict weak ordering between identities.
   306  // It returns a negative number if the first identity is "strictly less" than the second,
   307  // a positive number if the second identity is "strictly less" than the first,
   308  // and zero if the two identities are equal.
   309  //
   310  // `IdentityOrder` can be used to sort identities with
   311  // https://pkg.go.dev/golang.org/x/exp/slices#SortFunc.
   312  type IdentityOrder func(*Identity, *Identity) int
   313  
   314  // IdentityMapFunc is a modifier function for map operations for identities.
   315  // Identities are COPIED from the source slice.
   316  type IdentityMapFunc func(Identity) Identity
   317  
   318  // IdentityList is a list of nodes.
   319  type IdentityList []*Identity
   320  
   321  // Filter will apply a filter to the identity list.
   322  func (il IdentityList) Filter(filter IdentityFilter) IdentityList {
   323  	var dup IdentityList
   324  IDLoop:
   325  	for _, identity := range il {
   326  		if !filter(identity) {
   327  			continue IDLoop
   328  		}
   329  		dup = append(dup, identity)
   330  	}
   331  	return dup
   332  }
   333  
   334  // Map returns a new identity list with the map function f applied to a copy of
   335  // each identity.
   336  //
   337  // CAUTION: this relies on structure copy semantics. Map functions that modify
   338  // an object referenced by the input Identity structure will modify identities
   339  // in the source slice as well.
   340  func (il IdentityList) Map(f IdentityMapFunc) IdentityList {
   341  	dup := make(IdentityList, 0, len(il))
   342  	for _, identity := range il {
   343  		next := f(*identity)
   344  		dup = append(dup, &next)
   345  	}
   346  	return dup
   347  }
   348  
   349  // Copy returns a copy of the receiver. The resulting slice uses a different
   350  // backing array, meaning appends and insert operations on either slice are
   351  // guaranteed to only affect that slice.
   352  //
   353  // Copy should be used when modifying an existing identity list by either
   354  // appending new elements, re-ordering, or inserting new elements in an
   355  // existing index.
   356  func (il IdentityList) Copy() IdentityList {
   357  	dup := make(IdentityList, 0, len(il))
   358  
   359  	lenList := len(il)
   360  
   361  	// performance tests show this is faster than 'range'
   362  	for i := 0; i < lenList; i++ {
   363  		// copy the object
   364  		next := *(il[i])
   365  		dup = append(dup, &next)
   366  	}
   367  	return dup
   368  }
   369  
   370  // Selector returns an identity filter function that selects only identities
   371  // within this identity list.
   372  func (il IdentityList) Selector() IdentityFilter {
   373  
   374  	lookup := il.Lookup()
   375  	return func(identity *Identity) bool {
   376  		_, exists := lookup[identity.NodeID]
   377  		return exists
   378  	}
   379  }
   380  
   381  func (il IdentityList) Lookup() map[Identifier]*Identity {
   382  	lookup := make(map[Identifier]*Identity, len(il))
   383  	for _, identity := range il {
   384  		lookup[identity.NodeID] = identity
   385  	}
   386  	return lookup
   387  }
   388  
   389  // Sort will sort the list using the given ordering.  This is
   390  // not recommended for performance.  Expand the 'less' function
   391  // in place for best performance, and don't use this function.
   392  func (il IdentityList) Sort(less IdentityOrder) IdentityList {
   393  	dup := il.Copy()
   394  	slices.SortFunc(dup, less)
   395  	return dup
   396  }
   397  
   398  // NodeIDs returns the NodeIDs of the nodes in the list.
   399  func (il IdentityList) NodeIDs() IdentifierList {
   400  	nodeIDs := make([]Identifier, 0, len(il))
   401  	for _, id := range il {
   402  		nodeIDs = append(nodeIDs, id.NodeID)
   403  	}
   404  	return nodeIDs
   405  }
   406  
   407  // PublicStakingKeys returns a list with the public staking keys (order preserving).
   408  func (il IdentityList) PublicStakingKeys() []crypto.PublicKey {
   409  	pks := make([]crypto.PublicKey, 0, len(il))
   410  	for _, id := range il {
   411  		pks = append(pks, id.StakingPubKey)
   412  	}
   413  	return pks
   414  }
   415  
   416  // ID uniquely identifies a list of identities, by node ID. This can be used
   417  // to perpetually identify a group of nodes, even if mutable fields of some nodes
   418  // are changed, as node IDs are immutable.
   419  // CAUTION:
   420  //   - An IdentityList's ID is a cryptographic commitment to only node IDs. A node operator
   421  //     can freely choose the ID for their node. There is no relationship whatsoever between
   422  //     a node's ID and keys.
   423  //   - To generate a cryptographic commitment for the full IdentityList, use method `Checksum()`.
   424  //   - The outputs of `IdentityList.ID()` and `IdentityList.Checksum()` are both order-sensitive.
   425  //     Therefore, the `IdentityList` must be in canonical order, unless explicitly specified
   426  //     otherwise by the protocol.
   427  func (il IdentityList) ID() Identifier {
   428  	return il.NodeIDs().ID()
   429  }
   430  
   431  // Checksum generates a cryptographic commitment to the full IdentityList, including mutable fields.
   432  // The checksum for the same group of identities (by NodeID) may change from block to block.
   433  func (il IdentityList) Checksum() Identifier {
   434  	return MakeID(il)
   435  }
   436  
   437  // TotalWeight returns the total weight of all given identities.
   438  func (il IdentityList) TotalWeight() uint64 {
   439  	var total uint64
   440  	for _, identity := range il {
   441  		total += identity.Weight
   442  	}
   443  	return total
   444  }
   445  
   446  // Count returns the count of identities.
   447  func (il IdentityList) Count() uint {
   448  	return uint(len(il))
   449  }
   450  
   451  // ByIndex returns the node at the given index.
   452  func (il IdentityList) ByIndex(index uint) (*Identity, bool) {
   453  	if index >= uint(len(il)) {
   454  		return nil, false
   455  	}
   456  	return il[int(index)], true
   457  }
   458  
   459  // ByNodeID gets a node from the list by node ID.
   460  func (il IdentityList) ByNodeID(nodeID Identifier) (*Identity, bool) {
   461  	for _, identity := range il {
   462  		if identity.NodeID == nodeID {
   463  			return identity, true
   464  		}
   465  	}
   466  	return nil, false
   467  }
   468  
   469  // ByNetworkingKey gets a node from the list by network public key.
   470  func (il IdentityList) ByNetworkingKey(key crypto.PublicKey) (*Identity, bool) {
   471  	for _, identity := range il {
   472  		if identity.NetworkPubKey.Equals(key) {
   473  			return identity, true
   474  		}
   475  	}
   476  	return nil, false
   477  }
   478  
   479  // Sample returns non-deterministic random sample from the `IdentityList`
   480  func (il IdentityList) Sample(size uint) (IdentityList, error) {
   481  	n := uint(len(il))
   482  	dup := make([]*Identity, 0, n)
   483  	dup = append(dup, il...)
   484  	if n < size {
   485  		size = n
   486  	}
   487  	swap := func(i, j uint) {
   488  		dup[i], dup[j] = dup[j], dup[i]
   489  	}
   490  	err := rand.Samples(n, size, swap)
   491  	if err != nil {
   492  		return nil, fmt.Errorf("failed to sample identity list: %w", err)
   493  	}
   494  	return dup[:size], nil
   495  }
   496  
   497  // Shuffle randomly shuffles the identity list (non-deterministic),
   498  // and returns the shuffled list without modifying the receiver.
   499  func (il IdentityList) Shuffle() (IdentityList, error) {
   500  	return il.Sample(uint(len(il)))
   501  }
   502  
   503  // SamplePct returns a random sample from the receiver identity list. The
   504  // sample contains `pct` percentage of the list. The sample is rounded up
   505  // if `pct>0`, so this will always select at least one identity.
   506  //
   507  // NOTE: The input must be between 0-1.
   508  func (il IdentityList) SamplePct(pct float64) (IdentityList, error) {
   509  	if pct <= 0 {
   510  		return IdentityList{}, nil
   511  	}
   512  
   513  	count := float64(il.Count()) * pct
   514  	size := uint(math.Round(count))
   515  	// ensure we always select at least 1, for non-zero input
   516  	if size == 0 {
   517  		size = 1
   518  	}
   519  
   520  	return il.Sample(size)
   521  }
   522  
   523  // Union returns a new identity list containing every identity that occurs in
   524  // either `il`, or `other`, or both. There are no duplicates in the output,
   525  // where duplicates are identities with the same node ID.
   526  // The returned IdentityList is sorted canonically.
   527  func (il IdentityList) Union(other IdentityList) IdentityList {
   528  	maxLen := len(il) + len(other)
   529  
   530  	union := make(IdentityList, 0, maxLen)
   531  	set := make(map[Identifier]struct{}, maxLen)
   532  
   533  	for _, list := range []IdentityList{il, other} {
   534  		for _, id := range list {
   535  			if _, isDuplicate := set[id.NodeID]; !isDuplicate {
   536  				set[id.NodeID] = struct{}{}
   537  				union = append(union, id)
   538  			}
   539  		}
   540  	}
   541  
   542  	slices.SortFunc(union, Canonical)
   543  	return union
   544  }
   545  
   546  // EqualTo checks if the other list if the same, that it contains the same elements
   547  // in the same order
   548  func (il IdentityList) EqualTo(other IdentityList) bool {
   549  	return slices.EqualFunc(il, other, func(a, b *Identity) bool {
   550  		return a.EqualTo(b)
   551  	})
   552  }
   553  
   554  // Exists takes a previously sorted Identity list and searches it for the target value
   555  // This code is optimized, so the coding style will be different
   556  // target:  value to search for
   557  // CAUTION:  The identity list MUST be sorted prior to calling this method
   558  func (il IdentityList) Exists(target *Identity) bool {
   559  	return il.IdentifierExists(target.NodeID)
   560  }
   561  
   562  // IdentifierExists takes a previously sorted Identity list and searches it for the target value
   563  // target:  value to search for
   564  // CAUTION:  The identity list MUST be sorted prior to calling this method
   565  func (il IdentityList) IdentifierExists(target Identifier) bool {
   566  	_, ok := slices.BinarySearchFunc(il, &Identity{NodeID: target}, Canonical)
   567  	return ok
   568  }
   569  
   570  // GetIndex returns the index of the identifier in the IdentityList and true
   571  // if the identifier is found.
   572  func (il IdentityList) GetIndex(target Identifier) (uint, bool) {
   573  	i := slices.IndexFunc(il, func(a *Identity) bool {
   574  		return a.NodeID == target
   575  	})
   576  	if i == -1 {
   577  		return 0, false
   578  	}
   579  	return uint(i), true
   580  }