2023-04-25 16:09:55 +00:00
|
|
|
package enr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"math"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
|
2024-07-01 13:47:38 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2023-04-25 16:09:55 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
|
|
|
"github.com/multiformats/go-multiaddr"
|
2024-06-20 07:20:15 +00:00
|
|
|
"go.uber.org/zap"
|
2023-04-25 16:09:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func NewLocalnode(priv *ecdsa.PrivateKey) (*enode.LocalNode, error) {
|
|
|
|
db, err := enode.OpenDB("")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return enode.NewLocalNode(db, priv), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ENROption func(*enode.LocalNode) error
|
|
|
|
|
|
|
|
func WithMultiaddress(multiaddrs ...multiaddr.Multiaddr) ENROption {
|
|
|
|
return func(localnode *enode.LocalNode) (err error) {
|
|
|
|
// Randomly shuffle multiaddresses
|
|
|
|
rand.Shuffle(len(multiaddrs), func(i, j int) { multiaddrs[i], multiaddrs[j] = multiaddrs[j], multiaddrs[i] })
|
|
|
|
|
2024-07-01 13:47:38 +00:00
|
|
|
// Testing how many multiaddresses we can write before we exceed the limit
|
|
|
|
// By simulating what the localnode does when signing the enr, but without
|
|
|
|
// causing a panic
|
|
|
|
|
|
|
|
privk, err := crypto.GenerateKey()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-04-25 16:09:55 +00:00
|
|
|
// Adding extra multiaddresses. Should probably not exceed the enr max size of 300bytes
|
|
|
|
failedOnceWritingENR := false
|
|
|
|
couldWriteENRatLeastOnce := false
|
|
|
|
successIdx := -1
|
2023-09-01 21:52:04 +00:00
|
|
|
for i := len(multiaddrs); i > 0; i-- {
|
2024-07-01 13:47:38 +00:00
|
|
|
cpy := localnode.Node().Record() // Record() creates a copy for the current iteration
|
|
|
|
// Copy all the entries that might not have been written in the ENR record due to the
|
|
|
|
// async nature of localnode.Set
|
|
|
|
for _, entry := range localnode.Entries() {
|
|
|
|
cpy.Set(entry)
|
|
|
|
}
|
|
|
|
cpy.Set(enr.WithEntry(MultiaddrENRField, marshalMultiaddress(multiaddrs[0:i])))
|
|
|
|
cpy.SetSeq(localnode.Seq() + 1)
|
|
|
|
err = enode.SignV4(cpy, privk)
|
2023-04-25 16:09:55 +00:00
|
|
|
if err == nil {
|
|
|
|
couldWriteENRatLeastOnce = true
|
|
|
|
successIdx = i
|
|
|
|
break
|
|
|
|
}
|
2023-09-11 14:24:05 +00:00
|
|
|
failedOnceWritingENR = true
|
2023-04-25 16:09:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if failedOnceWritingENR && couldWriteENRatLeastOnce {
|
|
|
|
// Could write a subset of multiaddresses but not all
|
2024-07-01 13:47:38 +00:00
|
|
|
writeMultiaddressField(localnode, multiaddrs[0:successIdx])
|
2023-04-25 16:09:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithCapabilities(lightpush, filter, store, relay bool) ENROption {
|
|
|
|
return func(localnode *enode.LocalNode) (err error) {
|
|
|
|
wakuflags := NewWakuEnrBitfield(lightpush, filter, store, relay)
|
|
|
|
return WithWakuBitfield(wakuflags)(localnode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithWakuBitfield(flags WakuEnrBitfield) ENROption {
|
|
|
|
return func(localnode *enode.LocalNode) (err error) {
|
|
|
|
localnode.Set(enr.WithEntry(WakuENRField, flags))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithIP(ipAddr *net.TCPAddr) ENROption {
|
|
|
|
return func(localnode *enode.LocalNode) (err error) {
|
2024-06-20 07:20:15 +00:00
|
|
|
if ipAddr.Port == 0 {
|
|
|
|
return ErrNoPortAvailable
|
|
|
|
}
|
|
|
|
|
2023-04-25 16:09:55 +00:00
|
|
|
localnode.SetStaticIP(ipAddr.IP)
|
|
|
|
localnode.Set(enr.TCP(uint16(ipAddr.Port))) // TODO: ipv6?
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithUDPPort(udpPort uint) ENROption {
|
|
|
|
return func(localnode *enode.LocalNode) (err error) {
|
2024-03-25 18:11:41 +00:00
|
|
|
if udpPort == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-25 16:09:55 +00:00
|
|
|
if udpPort > math.MaxUint16 {
|
|
|
|
return errors.New("invalid udp port number")
|
|
|
|
}
|
|
|
|
localnode.SetFallbackUDP(int(udpPort))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-20 07:20:15 +00:00
|
|
|
func Update(logger *zap.Logger, localnode *enode.LocalNode, enrOptions ...ENROption) error {
|
2023-04-25 16:09:55 +00:00
|
|
|
for _, opt := range enrOptions {
|
|
|
|
err := opt(localnode)
|
|
|
|
if err != nil {
|
2024-06-20 07:20:15 +00:00
|
|
|
if errors.Is(err, ErrNoPortAvailable) {
|
|
|
|
logger.Warn("no tcp port available. ENR will not contain tcp key")
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
2023-04-25 16:09:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-01 13:47:38 +00:00
|
|
|
func marshalMultiaddress(addrAggr []multiaddr.Multiaddr) []byte {
|
2023-04-25 16:09:55 +00:00
|
|
|
var fieldRaw []byte
|
|
|
|
for _, addr := range addrAggr {
|
|
|
|
maRaw := addr.Bytes()
|
|
|
|
maSize := make([]byte, 2)
|
|
|
|
binary.BigEndian.PutUint16(maSize, uint16(len(maRaw)))
|
|
|
|
|
|
|
|
fieldRaw = append(fieldRaw, maSize...)
|
|
|
|
fieldRaw = append(fieldRaw, maRaw...)
|
|
|
|
}
|
2024-07-01 13:47:38 +00:00
|
|
|
return fieldRaw
|
|
|
|
}
|
2023-04-25 16:09:55 +00:00
|
|
|
|
2024-07-01 13:47:38 +00:00
|
|
|
func writeMultiaddressField(localnode *enode.LocalNode, addrAggr []multiaddr.Multiaddr) {
|
|
|
|
fieldRaw := marshalMultiaddress(addrAggr)
|
2024-06-20 07:20:15 +00:00
|
|
|
localnode.Set(enr.WithEntry(MultiaddrENRField, fieldRaw))
|
2024-07-01 13:47:38 +00:00
|
|
|
}
|
2023-04-25 16:09:55 +00:00
|
|
|
|
2024-07-01 13:47:38 +00:00
|
|
|
func DeleteField(localnode *enode.LocalNode, field string) {
|
|
|
|
localnode.Delete(enr.WithEntry(field, struct{}{}))
|
2023-04-25 16:09:55 +00:00
|
|
|
}
|