github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/bootstrap/node_info.go (about)

     1  // Package bootstrap defines canonical models and encoding for bootstrapping.
     2  package bootstrap
     3  
     4  import (
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/onflow/crypto"
    10  	"golang.org/x/exp/slices"
    11  
    12  	sdk "github.com/onflow/flow-go-sdk"
    13  	sdkcrypto "github.com/onflow/flow-go-sdk/crypto"
    14  
    15  	"github.com/onflow/flow-go/model/encodable"
    16  	"github.com/onflow/flow-go/model/flow"
    17  )
    18  
    19  // NodeInfoType enumerates the two different options for
    20  type NodeInfoType int
    21  
    22  const (
    23  	NodeInfoTypeInvalid NodeInfoType = iota
    24  	NodeInfoTypePublic
    25  	NodeInfoTypePrivate
    26  )
    27  
    28  const (
    29  	DefaultMachineAccountSignAlgo      = sdkcrypto.ECDSA_P256
    30  	DefaultMachineAccountHashAlgo      = sdkcrypto.SHA3_256
    31  	DefaultMachineAccountKeyIndex uint = 0
    32  )
    33  
    34  // ErrMissingPrivateInfo is returned when a method is called on NodeInfo
    35  // that is only valid on instances containing private info.
    36  var ErrMissingPrivateInfo = fmt.Errorf("can not access private information for a public node type")
    37  
    38  // NodeMachineAccountKey contains the private configration need to construct a
    39  // NodeMachineAccountInfo object. This is used as an intemediary by the bootstrap scripts
    40  // for storing the private key before generating a NodeMachineAccountInfo.
    41  type NodeMachineAccountKey struct {
    42  	PrivateKey encodable.MachineAccountPrivKey
    43  }
    44  
    45  // NodeMachineAccountInfo defines the structure for a bootstrapping file containing
    46  // private information about the node's machine account. The machine account is used
    47  // by the protocol software to interact with Flow as a client autonomously as needed, in
    48  // particular to run the DKG and generate root cluster quorum certificates when preparing
    49  // for an epoch.
    50  type NodeMachineAccountInfo struct {
    51  	// Address is the flow address of the machine account, not to be confused
    52  	// with the network address of the node.
    53  	Address string
    54  
    55  	// EncodedPrivateKey is the private key of the machine account
    56  	EncodedPrivateKey []byte
    57  
    58  	// KeyIndex is the index of the key in the associated machine account
    59  	KeyIndex uint
    60  
    61  	// SigningAlgorithm is the algorithm used by the machine account along with
    62  	// the above private key to create cryptographic signatures
    63  	SigningAlgorithm sdkcrypto.SignatureAlgorithm
    64  
    65  	// HashAlgorithm is the algorithm used for hashing
    66  	HashAlgorithm sdkcrypto.HashAlgorithm
    67  }
    68  
    69  func (info NodeMachineAccountInfo) FlowAddress() flow.Address {
    70  	// trim 0x-prefix if present
    71  	addr := info.Address
    72  	if strings.ToLower(addr[:2]) == "0x" {
    73  		addr = addr[2:]
    74  	}
    75  	return flow.HexToAddress(addr)
    76  }
    77  
    78  func (info NodeMachineAccountInfo) SDKAddress() sdk.Address {
    79  	flowAddr := info.FlowAddress()
    80  	var sdkAddr sdk.Address
    81  	copy(sdkAddr[:], flowAddr[:])
    82  	return sdkAddr
    83  }
    84  
    85  func (info NodeMachineAccountInfo) PrivateKey() (crypto.PrivateKey, error) {
    86  	sk, err := crypto.DecodePrivateKey(info.SigningAlgorithm, info.EncodedPrivateKey)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("could not decode machine account private key: %w", err)
    89  	}
    90  	return sk, nil
    91  }
    92  
    93  func (info NodeMachineAccountInfo) MustPrivateKey() crypto.PrivateKey {
    94  	sk, err := info.PrivateKey()
    95  	if err != nil {
    96  		panic(err)
    97  	}
    98  	return sk
    99  }
   100  
   101  // NodeConfig contains configuration information used as input to the
   102  // bootstrap process.
   103  type NodeConfig struct {
   104  	// Role is the flow role of the node (ex Collection, Consensus, ...)
   105  	Role flow.Role
   106  
   107  	// Address is the networking address of the node (IP:PORT), not to be
   108  	// confused with the address of the flow account associated with the node's
   109  	// machine account.
   110  	Address string
   111  
   112  	// Weight is the weight of the node
   113  	Weight uint64
   114  }
   115  
   116  // decodableNodeConfig provides backward-compatible decoding of old models
   117  // which use the Stake field in place of Weight.
   118  type decodableNodeConfig struct {
   119  	Role    flow.Role
   120  	Address string
   121  	Weight  uint64
   122  	// Stake previously was used in place of the Weight field.
   123  	Stake uint64
   124  }
   125  
   126  func (conf *NodeConfig) UnmarshalJSON(b []byte) error {
   127  	var decodable decodableNodeConfig
   128  	err := json.Unmarshal(b, &decodable)
   129  	if err != nil {
   130  		return fmt.Errorf("could not decode json: %w", err)
   131  	}
   132  	// compat: translate Stake fields to Weight
   133  	if decodable.Stake != 0 {
   134  		if decodable.Weight != 0 {
   135  			return fmt.Errorf("invalid NodeConfig with both Stake and Weight fields")
   136  		}
   137  		decodable.Weight = decodable.Stake
   138  	}
   139  	conf.Role = decodable.Role
   140  	conf.Address = decodable.Address
   141  	conf.Weight = decodable.Weight
   142  	return nil
   143  }
   144  
   145  // NodeInfoPriv defines the canonical structure for encoding private node info.
   146  type NodeInfoPriv struct {
   147  	Role           flow.Role
   148  	Address        string
   149  	NodeID         flow.Identifier
   150  	NetworkPrivKey encodable.NetworkPrivKey
   151  	StakingPrivKey encodable.StakingPrivKey
   152  }
   153  
   154  // NodeInfoPub defines the canonical structure for encoding public node info.
   155  type NodeInfoPub struct {
   156  	Role          flow.Role
   157  	Address       string
   158  	NodeID        flow.Identifier
   159  	Weight        uint64
   160  	NetworkPubKey encodable.NetworkPubKey
   161  	StakingPubKey encodable.StakingPubKey
   162  }
   163  
   164  // decodableNodeInfoPub provides backward-compatible decoding of old models
   165  // which use the Stake field in place of Weight.
   166  type decodableNodeInfoPub struct {
   167  	Role          flow.Role
   168  	Address       string
   169  	NodeID        flow.Identifier
   170  	Weight        uint64
   171  	NetworkPubKey encodable.NetworkPubKey
   172  	StakingPubKey encodable.StakingPubKey
   173  	// Stake previously was used in place of the Weight field.
   174  	// Deprecated: supported in decoding for backward-compatibility
   175  	Stake uint64
   176  }
   177  
   178  func (info *NodeInfoPub) Equals(other *NodeInfoPub) bool {
   179  	if other == nil {
   180  		return false
   181  	}
   182  	return info.Address == other.Address &&
   183  		info.NodeID == other.NodeID &&
   184  		info.Role == other.Role &&
   185  		info.Weight == other.Weight &&
   186  		info.NetworkPubKey.PublicKey.Equals(other.NetworkPubKey.PublicKey) &&
   187  		info.StakingPubKey.PublicKey.Equals(other.StakingPubKey.PublicKey)
   188  }
   189  
   190  func (info *NodeInfoPub) UnmarshalJSON(b []byte) error {
   191  	var decodable decodableNodeInfoPub
   192  	err := json.Unmarshal(b, &decodable)
   193  	if err != nil {
   194  		return fmt.Errorf("could not decode json: %w", err)
   195  	}
   196  	// compat: translate Stake fields to Weight
   197  	if decodable.Stake != 0 {
   198  		if decodable.Weight != 0 {
   199  			return fmt.Errorf("invalid NodeInfoPub with both Stake and Weight fields")
   200  		}
   201  		decodable.Weight = decodable.Stake
   202  	}
   203  	info.Role = decodable.Role
   204  	info.Address = decodable.Address
   205  	info.NodeID = decodable.NodeID
   206  	info.Weight = decodable.Weight
   207  	info.NetworkPubKey = decodable.NetworkPubKey
   208  	info.StakingPubKey = decodable.StakingPubKey
   209  	return nil
   210  }
   211  
   212  // NodePrivateKeys is a wrapper for the private keys for a node, comprising all
   213  // sensitive information for a node.
   214  type NodePrivateKeys struct {
   215  	StakingKey crypto.PrivateKey
   216  	NetworkKey crypto.PrivateKey
   217  }
   218  
   219  // NodeInfo contains information for a node. This is used during the bootstrapping
   220  // process to represent each node. When writing node information to disk, use
   221  // `Public` or `Private` to obtain the appropriate canonical structure.
   222  //
   223  // A NodeInfo instance can contain EITHER public keys OR private keys, not both.
   224  // This can be ensured by using only using the provided constructors and NOT
   225  // manually constructing an instance.
   226  type NodeInfo struct {
   227  
   228  	// NodeID is the unique identifier of the node in the network
   229  	NodeID flow.Identifier
   230  
   231  	// Role is the flow role of the node (collection, consensus, etc...)
   232  	Role flow.Role
   233  
   234  	// Address is the networking address of the node (IP:PORT), not to be
   235  	// confused with the address of the flow account associated with the node's
   236  	// machine account.
   237  	Address string
   238  
   239  	// Weight is the weight of the node
   240  	Weight uint64
   241  
   242  	// key information is private
   243  	networkPubKey  crypto.PublicKey
   244  	networkPrivKey crypto.PrivateKey
   245  	stakingPubKey  crypto.PublicKey
   246  	stakingPrivKey crypto.PrivateKey
   247  }
   248  
   249  func NewPublicNodeInfo(
   250  	nodeID flow.Identifier,
   251  	role flow.Role,
   252  	addr string,
   253  	weight uint64,
   254  	networkKey crypto.PublicKey,
   255  	stakingKey crypto.PublicKey,
   256  ) NodeInfo {
   257  	return NodeInfo{
   258  		NodeID:        nodeID,
   259  		Role:          role,
   260  		Address:       addr,
   261  		Weight:        weight,
   262  		networkPubKey: networkKey,
   263  		stakingPubKey: stakingKey,
   264  	}
   265  }
   266  
   267  func NewPrivateNodeInfo(
   268  	nodeID flow.Identifier,
   269  	role flow.Role,
   270  	addr string,
   271  	weight uint64,
   272  	networkKey crypto.PrivateKey,
   273  	stakingKey crypto.PrivateKey,
   274  ) NodeInfo {
   275  	return NodeInfo{
   276  		NodeID:         nodeID,
   277  		Role:           role,
   278  		Address:        addr,
   279  		Weight:         weight,
   280  		networkPrivKey: networkKey,
   281  		stakingPrivKey: stakingKey,
   282  		networkPubKey:  networkKey.PublicKey(),
   283  		stakingPubKey:  stakingKey.PublicKey(),
   284  	}
   285  }
   286  
   287  // Type returns the type of the node info instance.
   288  func (node NodeInfo) Type() NodeInfoType {
   289  	if node.networkPrivKey != nil && node.stakingPrivKey != nil {
   290  		return NodeInfoTypePrivate
   291  	}
   292  	if node.networkPubKey != nil && node.stakingPubKey != nil {
   293  		return NodeInfoTypePublic
   294  	}
   295  	return NodeInfoTypeInvalid
   296  }
   297  
   298  func (node NodeInfo) NetworkPubKey() crypto.PublicKey {
   299  	if node.networkPubKey != nil {
   300  		return node.networkPubKey
   301  	}
   302  	return node.networkPrivKey.PublicKey()
   303  }
   304  
   305  func (node NodeInfo) StakingPubKey() crypto.PublicKey {
   306  	if node.stakingPubKey != nil {
   307  		return node.stakingPubKey
   308  	}
   309  	return node.stakingPrivKey.PublicKey()
   310  }
   311  
   312  func (node NodeInfo) PrivateKeys() (*NodePrivateKeys, error) {
   313  	if node.Type() != NodeInfoTypePrivate {
   314  		return nil, ErrMissingPrivateInfo
   315  	}
   316  	return &NodePrivateKeys{
   317  		StakingKey: node.stakingPrivKey,
   318  		NetworkKey: node.networkPrivKey,
   319  	}, nil
   320  }
   321  
   322  // Private returns the canonical private encodable structure.
   323  func (node NodeInfo) Private() (NodeInfoPriv, error) {
   324  	if node.Type() != NodeInfoTypePrivate {
   325  		return NodeInfoPriv{}, ErrMissingPrivateInfo
   326  	}
   327  
   328  	return NodeInfoPriv{
   329  		Role:           node.Role,
   330  		Address:        node.Address,
   331  		NodeID:         node.NodeID,
   332  		NetworkPrivKey: encodable.NetworkPrivKey{PrivateKey: node.networkPrivKey},
   333  		StakingPrivKey: encodable.StakingPrivKey{PrivateKey: node.stakingPrivKey},
   334  	}, nil
   335  }
   336  
   337  // Public returns the canonical public encodable structure
   338  func (node NodeInfo) Public() NodeInfoPub {
   339  	return NodeInfoPub{
   340  		Role:          node.Role,
   341  		Address:       node.Address,
   342  		NodeID:        node.NodeID,
   343  		Weight:        node.Weight,
   344  		NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()},
   345  		StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()},
   346  	}
   347  }
   348  
   349  // PartnerPublic returns the public data for a partner node.
   350  func (node NodeInfo) PartnerPublic() PartnerNodeInfoPub {
   351  	return PartnerNodeInfoPub{
   352  		Role:          node.Role,
   353  		Address:       node.Address,
   354  		NodeID:        node.NodeID,
   355  		NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()},
   356  		StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()},
   357  	}
   358  }
   359  
   360  // Identity returns the node info as a public Flow identity.
   361  func (node NodeInfo) Identity() *flow.Identity {
   362  	identity := &flow.Identity{
   363  		IdentitySkeleton: flow.IdentitySkeleton{
   364  			NodeID:        node.NodeID,
   365  			Address:       node.Address,
   366  			Role:          node.Role,
   367  			InitialWeight: node.Weight,
   368  			StakingPubKey: node.stakingPubKey,
   369  			NetworkPubKey: node.networkPubKey,
   370  		},
   371  		DynamicIdentity: flow.DynamicIdentity{
   372  			EpochParticipationStatus: flow.EpochParticipationStatusActive,
   373  		},
   374  	}
   375  	return identity
   376  }
   377  
   378  // NodeInfoFromIdentity converts an identity to a public NodeInfo
   379  func NodeInfoFromIdentity(identity *flow.Identity) NodeInfo {
   380  	return NewPublicNodeInfo(
   381  		identity.NodeID,
   382  		identity.Role,
   383  		identity.Address,
   384  		identity.InitialWeight,
   385  		identity.NetworkPubKey,
   386  		identity.StakingPubKey)
   387  }
   388  
   389  func PrivateNodeInfoFromIdentity(identity *flow.Identity, networkKey, stakingKey crypto.PrivateKey) NodeInfo {
   390  	return NewPrivateNodeInfo(
   391  		identity.NodeID,
   392  		identity.Role,
   393  		identity.Address,
   394  		identity.InitialWeight,
   395  		networkKey,
   396  		stakingKey,
   397  	)
   398  }
   399  
   400  func FilterByRole(nodes []NodeInfo, role flow.Role) []NodeInfo {
   401  	var filtered []NodeInfo
   402  	for _, node := range nodes {
   403  		if node.Role != role {
   404  			continue
   405  		}
   406  		filtered = append(filtered, node)
   407  	}
   408  	return filtered
   409  }
   410  
   411  // Sort sorts the NodeInfo list using the given ordering.
   412  //
   413  // The sorted list is returned and the original list is untouched.
   414  func Sort(nodes []NodeInfo, order flow.IdentityOrder[flow.Identity]) []NodeInfo {
   415  	dup := make([]NodeInfo, len(nodes))
   416  	copy(dup, nodes)
   417  	slices.SortFunc(dup, func(i, j NodeInfo) int {
   418  		return order(i.Identity(), j.Identity())
   419  	})
   420  	return dup
   421  }
   422  
   423  func ToIdentityList(nodes []NodeInfo) flow.IdentityList {
   424  	il := make(flow.IdentityList, 0, len(nodes))
   425  	for _, node := range nodes {
   426  		il = append(il, node.Identity())
   427  	}
   428  	return il
   429  }
   430  
   431  func ToPublicNodeInfoList(nodes []NodeInfo) []NodeInfoPub {
   432  	pub := make([]NodeInfoPub, 0, len(nodes))
   433  	for _, node := range nodes {
   434  		pub = append(pub, node.Public())
   435  	}
   436  	return pub
   437  }