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 }