Files
p2p/peer/ip_signer.go
zeekayandHanzo Dev 10a16cfec8 Depend on the renamed utility package
github.com/luxfi/utils is now github.com/luxfi/util. Import paths and the
module requirement follow the rename.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-06 04:16:45 -07:00

94 lines
2.4 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package peer
import (
"crypto"
"net/netip"
"sync"
"github.com/luxfi/crypto/bls"
"github.com/luxfi/node/utils/timer/mockable"
"github.com/luxfi/util"
)
// IPSigner will return a signedIP for the current value of our dynamic IP.
type IPSigner struct {
ip *utils.Atomic[netip.AddrPort]
clock mockable.Clock
tlsSigner crypto.Signer
blsSigner bls.Signer
// Must be held while accessing [signedIP]
signedIPLock sync.RWMutex
// Note that the values in [*signedIP] are constants and can be inspected
// without holding [signedIPLock].
signedIP *SignedIP
}
func NewIPSigner(
ip *utils.Atomic[netip.AddrPort],
tlsSigner crypto.Signer,
blsSigner bls.Signer,
) *IPSigner {
return &IPSigner{
ip: ip,
tlsSigner: tlsSigner,
blsSigner: blsSigner,
}
}
// GetSignedIP returns the signedIP of the current value of the provided
// dynamicIP. If the dynamicIP hasn't changed since the prior call to
// GetSignedIP, then the same [SignedIP] will be returned.
//
// It's safe for multiple goroutines to concurrently call GetSignedIP.
func (s *IPSigner) GetSignedIP() (*SignedIP, error) {
// Optimistically, the IP should already be signed. By grabbing a read lock
// here we enable full concurrency of new connections.
s.signedIPLock.RLock()
signedIP := s.signedIP
s.signedIPLock.RUnlock()
ip := s.ip.Get()
if signedIP != nil && signedIP.AddrPort == ip {
return signedIP, nil
}
// If our current IP hasn't been signed yet - then we should sign it.
s.signedIPLock.Lock()
defer s.signedIPLock.Unlock()
// It's possible that multiple threads read [n.signedIP] as incorrect at the
// same time, we should verify that we are the first thread to attempt to
// update it.
signedIP = s.signedIP
if signedIP != nil && signedIP.AddrPort == ip {
return signedIP, nil
}
// We should now sign our new IP at the current timestamp.
unsignedIP := UnsignedIP{
AddrPort: ip,
Timestamp: s.clock.Unix(),
}
signedIP, err := unsignedIP.Sign(s.tlsSigner, s.blsSigner)
if err != nil {
return nil, err
}
s.signedIP = signedIP
return s.signedIP, nil
}
// PublicKey returns the BLS public key for this IPSigner
func (s *IPSigner) PublicKey() *bls.PublicKey {
if s.blsSigner == nil {
return nil
}
return s.blsSigner.PublicKey()
}