github.com/MetalBlockchain/metalgo@v1.11.9/network/peer/ip_signer.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package peer 5 6 import ( 7 "crypto" 8 "net/netip" 9 "sync" 10 11 "github.com/MetalBlockchain/metalgo/utils" 12 "github.com/MetalBlockchain/metalgo/utils/crypto/bls" 13 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 14 ) 15 16 // IPSigner will return a signedIP for the current value of our dynamic IP. 17 type IPSigner struct { 18 ip *utils.Atomic[netip.AddrPort] 19 clock mockable.Clock 20 tlsSigner crypto.Signer 21 blsSigner *bls.SecretKey 22 23 // Must be held while accessing [signedIP] 24 signedIPLock sync.RWMutex 25 // Note that the values in [*signedIP] are constants and can be inspected 26 // without holding [signedIPLock]. 27 signedIP *SignedIP 28 } 29 30 func NewIPSigner( 31 ip *utils.Atomic[netip.AddrPort], 32 tlsSigner crypto.Signer, 33 blsSigner *bls.SecretKey, 34 ) *IPSigner { 35 return &IPSigner{ 36 ip: ip, 37 tlsSigner: tlsSigner, 38 blsSigner: blsSigner, 39 } 40 } 41 42 // GetSignedIP returns the signedIP of the current value of the provided 43 // dynamicIP. If the dynamicIP hasn't changed since the prior call to 44 // GetSignedIP, then the same [SignedIP] will be returned. 45 // 46 // It's safe for multiple goroutines to concurrently call GetSignedIP. 47 func (s *IPSigner) GetSignedIP() (*SignedIP, error) { 48 // Optimistically, the IP should already be signed. By grabbing a read lock 49 // here we enable full concurrency of new connections. 50 s.signedIPLock.RLock() 51 signedIP := s.signedIP 52 s.signedIPLock.RUnlock() 53 ip := s.ip.Get() 54 if signedIP != nil && signedIP.AddrPort == ip { 55 return signedIP, nil 56 } 57 58 // If our current IP hasn't been signed yet - then we should sign it. 59 s.signedIPLock.Lock() 60 defer s.signedIPLock.Unlock() 61 62 // It's possible that multiple threads read [n.signedIP] as incorrect at the 63 // same time, we should verify that we are the first thread to attempt to 64 // update it. 65 signedIP = s.signedIP 66 if signedIP != nil && signedIP.AddrPort == ip { 67 return signedIP, nil 68 } 69 70 // We should now sign our new IP at the current timestamp. 71 unsignedIP := UnsignedIP{ 72 AddrPort: ip, 73 Timestamp: s.clock.Unix(), 74 } 75 signedIP, err := unsignedIP.Sign(s.tlsSigner, s.blsSigner) 76 if err != nil { 77 return nil, err 78 } 79 80 s.signedIP = signedIP 81 return s.signedIP, nil 82 }