diff --git a/les/serverpool.go b/les/serverpool.go index 12688f62f..0fe6e49b6 100644 --- a/les/serverpool.go +++ b/les/serverpool.go @@ -191,7 +191,7 @@ func (pool *serverPool) discoverNodes() { if err != nil { continue } - pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP), 0) + pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP)) } } @@ -667,7 +667,7 @@ func (e *poolEntry) DecodeRLP(s *rlp.Stream) error { return err } addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()} - e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port), 0) + e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port)) e.addr = make(map[string]*poolEntryAddress) e.addr[addr.strKey()] = addr e.addrSelect = *newWeightedRandomSelect() diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index d8431209b..6b4cd2d18 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -57,7 +57,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding // Fill up the sender's bucket. pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") - pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99, 0)) + pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99)) last := fillBucket(tab, pingSender) // Add the sender as if it just pinged us. Revalidate should replace the last node in @@ -289,7 +289,7 @@ func TestTable_Lookup(t *testing.T) { } // seed table with initial node (otherwise lookup will terminate immediately) seedKey, _ := decodePubkey(lookupTestnet.dists[256][0]) - seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256, 0)) + seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256)) tab.stuff([]*node{seed}) results := tab.lookup(lookupTestnet.target, true) @@ -524,7 +524,7 @@ func (tn *preminedTestnet) findnode(toid enode.ID, toaddr *net.UDPAddr, target e var result []*node for i, ekey := range tn.dists[toaddr.Port] { key, _ := decodePubkey(ekey) - node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next, 0)) + node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next)) result = append(result, node) } return result, nil diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go index cb1e67387..37a044902 100644 --- a/p2p/discover/udp.go +++ b/p2p/discover/udp.go @@ -141,7 +141,7 @@ func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*node, error) { if err != nil { return nil, err } - n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP), 0)) + n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP))) err = n.ValidateComplete() return n, err } @@ -628,7 +628,7 @@ func (req *ping) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte ReplyTok: mac, Expiration: uint64(time.Now().Add(expiration).Unix()), }) - n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port, 0)) + n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port)) t.handleReply(n.ID(), pingPacket, req) if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration { t.sendPing(n.ID(), from, func() { t.tab.addThroughPing(n) }) diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 7c0cc7523..768296300 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -244,7 +244,7 @@ func TestUDP_findnode(t *testing.T) { nodes := &nodesByDistance{target: testTarget.id()} for i := 0; i < bucketSize; i++ { key := newkey() - n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i, 0)) + n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i)) nodes.push(n, bucketSize) } test.table.stuff(nodes.entries) @@ -311,7 +311,7 @@ func TestUDP_findnodeMultiReply(t *testing.T) { rpclist := make([]rpcNode, len(list)) for i := range list { rpclist[i] = nodeToRPC(list[i]) - list[i] = wrapNode(enode.NewV4(list[i].Pubkey(), list[i].IP(), list[i].TCP(), list[i].UDP(), 0)) + list[i] = wrapNode(enode.NewV4(list[i].Pubkey(), list[i].IP(), list[i].TCP(), list[i].UDP())) } test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]}) test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]}) diff --git a/p2p/enode/node.go b/p2p/enode/node.go index dced1b6ff..0e285f406 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -21,7 +21,6 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/ethereum/go-ethereum/log" "math/bits" "math/rand" "net" @@ -59,9 +58,10 @@ func (n *Node) Seq() uint64 { return n.r.Seq() } -// Incomplete returns true for nodes with no IP address. +// Quorum +// Incomplete returns true for nodes with no IP address and no hostname if with raftport. func (n *Node) Incomplete() bool { - return n.IP() == nil + return n.IP() == nil && (!n.HasRaftPort() || (n.Host() == "" && n.HasRaftPort())) } // Load retrieves an entry from the underlying record. @@ -70,21 +70,27 @@ func (n *Node) Load(k enr.Entry) error { } // IP returns the IP address of the node. +// +// Quorum +// To support DNS lookup in node ip. The function performs hostname lookup if hostname is defined in enr.Hostname +// and falls back to enr.IP value in case of failure. It also makes sure the resolved IP is in IPv4 or IPv6 format func (n *Node) IP() net.IP { - // QUORUM - // no host is set, so use the IP directly if n.Host() == "" { + // no host is set, so use the IP directly return n.loadIP() } // attempt to look up IP addresses if host is a FQDN lookupIPs, err := net.LookupIP(n.Host()) if err != nil { - log.Debug("hostname couldn't resolve, using IP instead", "hostname", n.Host(), "err", err.Error()) return n.loadIP() } - // set to first ip by default - return lookupIPs[0] - // END QUORUM + // set to first ip by default & as Ethereum upstream + ip := lookupIPs[0] + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + return ip } func (n *Node) loadIP() net.IP { @@ -99,6 +105,7 @@ func (n *Node) Host() string { n.Load((*enr.Hostname)(&hostname)) return hostname } + // End-Quorum // UDP returns the UDP port of the node. diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index 861a70bd6..0398b38ed 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -19,6 +19,7 @@ package enode import ( "encoding/hex" "fmt" + "net" "testing" "github.com/ethereum/go-ethereum/p2p/enr" @@ -60,3 +61,84 @@ func TestPythonInterop(t *testing.T) { } } } + +// Quorum +// test Incomplete +func TestIncomplete(t *testing.T) { + var testCases = []struct { + n *Node + isIncomplete bool + }{ + { + n: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 1}, + 52150, + 52150, + ), + isIncomplete: false, + }, + { + n: NewV4(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("::"), + 52150, + 52150, + ), + isIncomplete: false, + }, + { + n: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), + 52150, + 52150, + ), + isIncomplete: false, + }, + { + n: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, + 52150, + 52150, + ), + isIncomplete: true, + }, + { + n: NewV4Hostname( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + "hostname", + 52150, + 52150, + 50400, + ), + isIncomplete: false, + }, + { + n: NewV4Hostname( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + "hostname", + 52150, + 52150, + 0, + ), + isIncomplete: true, + }, + { + n: NewV4Hostname( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + "", + 52150, + 52150, + 50400, + ), + isIncomplete: true, + }, + } + + for i, test := range testCases { + if test.n.Incomplete() != test.isIncomplete { + t.Errorf("test %d: Node.Incomplete() mismatch:\ngot: %v\nwant: %v", i, test.n.Incomplete(), test.isIncomplete) + } + } +} diff --git a/p2p/enode/nodedb_test.go b/p2p/enode/nodedb_test.go index 9e1cb86d6..96794827c 100644 --- a/p2p/enode/nodedb_test.go +++ b/p2p/enode/nodedb_test.go @@ -105,7 +105,6 @@ func TestDBFetchStore(t *testing.T) { net.IP{192, 168, 0, 1}, 30303, 30303, - 0, ) inst := time.Now() num := 314 @@ -169,7 +168,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 3}, 30303, 30303, - 0, ), pong: time.Now().Add(-3 * time.Hour), }, @@ -181,7 +179,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 3}, 30303, 30303, - 0, ), pong: time.Now().Add(-4 * time.Second), }, @@ -193,7 +190,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 1}, 30303, 30303, - 0, ), pong: time.Now().Add(-2 * time.Second), }, @@ -203,7 +199,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 2}, 30303, 30303, - 0, ), pong: time.Now().Add(-3 * time.Second), }, @@ -213,7 +208,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 3}, 30303, 30303, - 0, ), pong: time.Now().Add(-1 * time.Second), }, @@ -223,7 +217,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 3}, 30303, 30303, - 0, ), pong: time.Now().Add(-2 * time.Second), }, @@ -233,7 +226,6 @@ var nodeDBSeedQueryNodes = []struct { net.IP{127, 0, 0, 3}, 30303, 30303, - 0, ), pong: time.Now().Add(-2 * time.Second), }, @@ -339,7 +331,6 @@ var nodeDBExpirationNodes = []struct { net.IP{127, 0, 0, 1}, 30303, 30303, - 0, ), pong: time.Now().Add(-dbNodeExpiration + time.Minute), exp: false, @@ -349,7 +340,6 @@ var nodeDBExpirationNodes = []struct { net.IP{127, 0, 0, 2}, 30303, 30303, - 0, ), pong: time.Now().Add(-dbNodeExpiration - time.Minute), exp: true, diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go index 81cdf664e..7d952578c 100644 --- a/p2p/enode/urlv4.go +++ b/p2p/enode/urlv4.go @@ -56,7 +56,7 @@ func MustParseV4(rawurl string) *Node { // // For complete nodes, the node ID is encoded in the username portion // of the URL, separated from the host by an @ sign. The hostname can -// only be given as an IP address, DNS domain names are not allowed. +// be given as an IP address or a DNS domain name. // The port in the host name section is the TCP listening port. If the // TCP and UDP (discovery) ports differ, the UDP port is specified as // query parameter "discport". @@ -70,37 +70,32 @@ func ParseV4(rawurl string) (*Node, error) { if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { id, err := parsePubkey(m[1]) if err != nil { - return nil, fmt.Errorf("invalid node ID (%v)", err) + return nil, fmt.Errorf("invalid public key (%v)", err) } - return NewV4(id, nil, 0, 0, 0), nil + return NewV4(id, nil, 0, 0), nil } return parseComplete(rawurl) } // NewV4 creates a node from discovery v4 node information. The record // contained in the node has a zero-length signature. -func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp, raftPort int) *Node { +func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node { var r enr.Record - if ip != nil { + if len(ip) > 0 { r.Set(enr.IP(ip)) } - return newV4(pubkey, r, tcp, udp, raftPort) + return newV4(pubkey, r, tcp, udp) } // broken out from `func NewV4` (above) same in upstream go-ethereum, but taken out // to avoid code duplication b/t NewV4 and NewV4Hostname -func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp, raftPort int) *Node { +func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp int) *Node { if udp != 0 { r.Set(enr.UDP(udp)) } if tcp != 0 { r.Set(enr.TCP(tcp)) } - - if raftPort != 0 { // Quorum - r.Set(enr.RaftPort(raftPort)) - } - signV4Compat(&r, pubkey) n, err := New(v4CompatID{}, &r) if err != nil { @@ -112,14 +107,22 @@ func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp, raftPort int) *Node // Quorum // NewV4Hostname creates a node from discovery v4 node information. The record -// contained in the node has a zero-length signature. It sets the hostname of -// the node instead of the IP address. +// contained in the node has a zero-length signature. It sets the hostname or ip +// of the node depends on hostname context func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort int) *Node { var r enr.Record - if hostname != "" { + + if ip := net.ParseIP(hostname); ip == nil { r.Set(enr.Hostname(hostname)) + } else { + r.Set(enr.IP(ip)) } - return newV4(pubkey, r, tcp, udp, raftPort) + + if raftPort != 0 { + r.Set(enr.RaftPort(raftPort)) + } + + return newV4(pubkey, r, tcp, udp) } // End-Quorum @@ -127,6 +130,7 @@ func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort func parseComplete(rawurl string) (*Node, error) { var ( id *ecdsa.PublicKey + ip net.IP tcpPort, udpPort uint64 ) u, err := url.Parse(rawurl) @@ -141,14 +145,29 @@ func parseComplete(rawurl string) (*Node, error) { return nil, errors.New("does not contain node ID") } if id, err = parsePubkey(u.User.String()); err != nil { - return nil, fmt.Errorf("invalid node ID (%v)", err) + return nil, fmt.Errorf("invalid public key (%v)", err) + } + // move qv up to here + qv := u.Query() + // Parse the IP address. + ips, err := net.LookupIP(u.Hostname()) + if err != nil { + // Quorum: if IP look up fail don't return error for raft url + if qv.Get("raftport") == "" { + return nil, err + } + } else { + ip = ips[0] + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } } // Parse the port numbers. if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil { return nil, errors.New("invalid port") } udpPort = tcpPort - qv := u.Query() if qv.Get("discport") != "" { udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) if err != nil { @@ -156,21 +175,20 @@ func parseComplete(rawurl string) (*Node, error) { } } - var node *Node - // Quorum if qv.Get("raftport") != "" { raftPort, err := strconv.ParseUint(qv.Get("raftport"), 10, 16) if err != nil { return nil, errors.New("invalid raftport in query") } - node = NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort)) - } else { - node = NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), 0) + if u.Hostname() == "" { + return nil, errors.New("empty hostname in raft url") + } + return NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort)), nil } // End-Quorum - return node, nil + return NewV4(id, ip, int(tcpPort), int(udpPort)), nil } func HexPubkey(h string) (*ecdsa.PublicKey, error) { @@ -229,9 +247,14 @@ func (n *Node) v4URL() string { if n.Incomplete() { u.Host = nodeid } else { - addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} u.User = url.User(nodeid) - u.Host = addr.String() + if n.Host() != "" && net.ParseIP(n.Host()) == nil { + // Quorum + u.Host = net.JoinHostPort(n.Host(), strconv.Itoa(n.TCP())) + } else { + addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} + u.Host = addr.String() + } if n.UDP() != n.TCP() { u.RawQuery = "discport=" + strconv.Itoa(n.UDP()) } diff --git a/p2p/enode/urlv4_test.go b/p2p/enode/urlv4_test.go index 9b76bb9ec..1e450e23f 100644 --- a/p2p/enode/urlv4_test.go +++ b/p2p/enode/urlv4_test.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/ecdsa" "math/big" + "net" "reflect" "strings" "testing" @@ -37,7 +38,7 @@ var parseNodeTests = []struct { }, { rawurl: "enode://01010101@123.124.125.126:3", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, + wantError: `invalid public key (wrong length, want 128 hex chars)`, }, // Complete nodes with IP address. { @@ -50,42 +51,38 @@ var parseNodeTests = []struct { }, { rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", - wantResult: NewV4Hostname( + wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - "127.0.0.1", + net.IP{127, 0, 0, 1}, 52150, 52150, - 0, ), }, { rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", - wantResult: NewV4Hostname( + wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - "::", + net.ParseIP("::"), 52150, 52150, - 0, ), }, { rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", - wantResult: NewV4Hostname( + wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - "2001:db8:3c4d:15::abcd:ef12", + net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 52150, - 0, ), }, { rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", - wantResult: NewV4Hostname( + wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - "127.0.0.1", + net.IP{0x7f, 0x0, 0x0, 0x1}, 52150, 22334, - 0, ), }, // Incomplete nodes with no address. @@ -93,30 +90,45 @@ var parseNodeTests = []struct { rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - nil, 0, 0, 0, + nil, 0, 0, ), }, { rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), - nil, 0, 0, 0, + nil, 0, 0, ), }, // Invalid URLs { rawurl: "01010101", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, + wantError: `invalid public key (wrong length, want 128 hex chars)`, }, { rawurl: "enode://01010101", - wantError: `invalid node ID (wrong length, want 128 hex chars)`, + wantError: `invalid public key (wrong length, want 128 hex chars)`, }, { // This test checks that errors from url.Parse are handled. rawurl: "://foo", wantError: `parse ://foo: missing protocol scheme`, }, + { + // Quorum: raft url with invalid hostname (no error, hostname will be saved) + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3?raftport=50401", + wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "hostname", 3, 3, 50401), + }, + { + // Quorum: raft url with valid hostname + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@localhost:3?raftport=50401", + wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "localhost", 3, 3, 50401), + }, + { + // Quorum: raft url with no hostname + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@:3?raftport=50401", + wantError: `empty hostname in raft url`, + }, } func hexPubkey(h string) *ecdsa.PublicKey { @@ -128,20 +140,7 @@ func hexPubkey(h string) *ecdsa.PublicKey { } func TestParseNode(t *testing.T) { - extraTests := []struct { - rawurl string - wantError string - wantResult *Node - }{ - { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", - wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "hostname", 3, 3, 0, ), - }, - } - - testNodes := append(parseNodeTests, extraTests...) - - for _, test := range testNodes { + for _, test := range parseNodeTests { n, err := ParseV4(test.rawurl) if test.wantError != "" { if err == nil { diff --git a/p2p/server.go b/p2p/server.go index 74b251288..257e2dee0 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -363,7 +363,7 @@ func (srv *Server) Self() *enode.Node { srv.lock.Unlock() if ln == nil { - return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0, 0) + return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0) } return ln.Node() } @@ -1007,7 +1007,7 @@ func nodeFromConn(pubkey *ecdsa.PublicKey, conn net.Conn) *enode.Node { ip = tcp.IP port = tcp.Port } - return enode.NewV4(pubkey, ip, port, port, 0) + return enode.NewV4(pubkey, ip, port, port) } func truncateName(s string) string { diff --git a/p2p/server_test.go b/p2p/server_test.go index 34e0962d1..4c49dbb12 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -152,7 +152,7 @@ func TestServerDial(t *testing.T) { // tell the server to connect tcpAddr := listener.Addr().(*net.TCPAddr) - node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0, 0) + node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0) srv.AddPeer(node) select { @@ -422,7 +422,7 @@ func TestServerAtCap(t *testing.T) { func TestServerPeerLimits(t *testing.T) { srvkey := newkey() clientkey := newkey() - clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0, 0) + clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0) var tp = &setupTransport{ pubkey: &clientkey.PublicKey, @@ -512,21 +512,21 @@ func TestServerSetupConn(t *testing.T) { }, { tt: &setupTransport{pubkey: clientpub}, - dialDest: enode.NewV4(&newkey().PublicKey, nil, 0, 0, 0), + dialDest: enode.NewV4(&newkey().PublicKey, nil, 0, 0), flags: dynDialedConn, wantCalls: "doEncHandshake,close,", wantCloseErr: DiscUnexpectedIdentity, }, { tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}}, - dialDest: enode.NewV4(clientpub, nil, 0, 0, 0), + dialDest: enode.NewV4(clientpub, nil, 0, 0), flags: dynDialedConn, wantCalls: "doEncHandshake,doProtoHandshake,close,", wantCloseErr: DiscUnexpectedIdentity, }, { tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")}, - dialDest: enode.NewV4(clientpub, nil, 0, 0, 0), + dialDest: enode.NewV4(clientpub, nil, 0, 0), flags: dynDialedConn, wantCalls: "doEncHandshake,doProtoHandshake,close,", wantCloseErr: errors.New("foo"), @@ -578,7 +578,7 @@ func TestServerSetupConn_whenNotInRaftCluster(t *testing.T) { clientpub = &clientkey.PublicKey ) - clientNode := enode.NewV4(clientpub, nil, 0, 0, 0) + clientNode := enode.NewV4(clientpub, nil, 0, 0) srv := &Server{ Config: Config{ PrivateKey: srvkey, @@ -616,7 +616,7 @@ func TestServerSetupConn_whenNotPermissioned(t *testing.T) { clientkey, srvkey = newkey(), newkey() clientpub = &clientkey.PublicKey ) - clientNode := enode.NewV4(clientpub, nil, 0, 0, 0) + clientNode := enode.NewV4(clientpub, nil, 0, 0) srv := &Server{ Config: Config{ PrivateKey: srvkey, diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index f03eec545..6681726e4 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -165,7 +165,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error { // Node returns the node descriptor represented by the config. func (n *NodeConfig) Node() *enode.Node { - return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port), 0) + return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port)) } // RandomNodeConfig returns node configuration with a randomly generated ID and diff --git a/raft/handler.go b/raft/handler.go index 8ee70b7b3..6a74a92d4 100755 --- a/raft/handler.go +++ b/raft/handler.go @@ -325,16 +325,19 @@ func (pm *ProtocolManager) ProposeNewPeer(enodeId string, isLearner bool) (uint1 if pm.isLearnerNode() { return 0, errors.New("learner node can't add peer or learner") } - parsedUrl, _ := url.Parse(enodeId) node, err := enode.ParseV4(enodeId) if err != nil { return 0, err } - //use the hostname instead of the IP, since if DNS is not enabled, the hostname should *be* the IP - ip := net.ParseIP(parsedUrl.Hostname()) - if !pm.useDns && (len(ip.To4()) != 4) { - return 0, fmt.Errorf("expected IPv4 address (with length 4), but got IP of length %v", len(node.IP())) + if !pm.useDns { + // hostname is not allowed if DNS is not enabled + if node.Host() != "" { + return 0, fmt.Errorf("raft must enable dns to use hostname") + } + if len(node.IP()) != 4 { + return 0, fmt.Errorf("expected IPv4 address (with length 4), but got IP of length %v", len(node.IP())) + } } if !node.HasRaftPort() { @@ -357,7 +360,7 @@ func (pm *ProtocolManager) ProposeNewPeer(enodeId string, isLearner bool) (uint1 pm.confChangeProposalC <- raftpb.ConfChange{ Type: confChangeType, NodeID: uint64(raftId), - Context: address.toBytes(pm.useDns), + Context: address.toBytes(), } return raftId, nil @@ -768,11 +771,6 @@ func (pm *ProtocolManager) entriesToApply(allEntries []raftpb.Entry) (entriesToA } func (pm *ProtocolManager) raftUrl(address *Address) string { - if !pm.useDns { - parsedIp := net.ParseIP(address.Hostname) - return fmt.Sprintf("http://%s:%d", parsedIp.To4(), address.RaftPort) - } - if parsedIp := net.ParseIP(address.Hostname); parsedIp != nil { if ipv4 := parsedIp.To4(); ipv4 != nil { //this is an IPv4 address @@ -1001,7 +999,7 @@ func (pm *ProtocolManager) makeInitialRaftPeers() (raftPeers []etcdRaft.Peer, pe address := newAddress(raftId, node.RaftPort(), node, pm.useDns) raftPeers[i] = etcdRaft.Peer{ ID: uint64(raftId), - Context: address.toBytes(pm.useDns), + Context: address.toBytes(), } if raftId == pm.raftId { diff --git a/raft/handler_test.go b/raft/handler_test.go index 9ccca0377..433fba32a 100644 --- a/raft/handler_test.go +++ b/raft/handler_test.go @@ -45,7 +45,7 @@ func TestProtocolManager_whenAppliedIndexOutOfSync(t *testing.T) { for i := 0; i < count; i++ { ports[i] = nextPort(t) nodeKeys[i] = mustNewNodeKey(t) - peers[i] = enode.NewV4(&(nodeKeys[i].PublicKey), net.IPv4(127, 0, 0, 1), 0, 0, int(ports[i])) + peers[i] = enode.NewV4Hostname(&(nodeKeys[i].PublicKey), net.IPv4(127, 0, 0, 1).String(), 0, 0, int(ports[i])) } raftNodes := make([]*RaftService, count) for i := 0; i < count; i++ { diff --git a/raft/peer.go b/raft/peer.go index e5c5c8476..392d2c03e 100644 --- a/raft/peer.go +++ b/raft/peer.go @@ -33,13 +33,13 @@ type ClusterInfo struct { NodeActive bool `json:"nodeActive"` } -func newAddress(raftId uint16, raftPort int, node *enode.Node, withHostname bool) *Address { +func newAddress(raftId uint16, raftPort int, node *enode.Node, useDns bool) *Address { // derive 64 byte nodeID from 128 byte enodeID id, err := enode.RaftHexID(node.EnodeID()) if err != nil { panic(err) } - if withHostname { + if useDns && node.Host() != "" { return &Address{ RaftId: raftId, NodeId: id, @@ -66,13 +66,14 @@ type Peer struct { } // RLP Address encoding, for transport over raft and storage in LevelDB. -func (addr *Address) toBytes(withHostname bool) []byte { +func (addr *Address) toBytes() []byte { var toEncode interface{} - if withHostname { + // need to check if addr.Hostname is hostname/ip + if ip := net.ParseIP(addr.Hostname); ip == nil { toEncode = addr } else { - toEncode = []interface{}{addr.RaftId, addr.NodeId, net.ParseIP(addr.Hostname), addr.P2pPort, addr.RaftPort} + toEncode = []interface{}{addr.RaftId, addr.NodeId, ip, addr.P2pPort, addr.RaftPort} } buffer, err := rlp.EncodeToBytes(toEncode) @@ -83,14 +84,14 @@ func (addr *Address) toBytes(withHostname bool) []byte { } func bytesToAddress(input []byte) *Address { - //try the new format first + // try the new format first addr := new(Address) streamNew := rlp.NewStream(bytes.NewReader(input), 0) if err := streamNew.Decode(addr); err == nil { return addr } - //else try the old format + // else try the old format var temp struct { RaftId uint16 NodeId enode.EnodeID diff --git a/raft/snapshot.go b/raft/snapshot.go index 30dc5a2a2..14bbdfd79 100644 --- a/raft/snapshot.go +++ b/raft/snapshot.go @@ -97,7 +97,7 @@ func (pm *ProtocolManager) triggerSnapshot(index uint64) { //snapData := pm.blockchain.CurrentBlock().Hash().Bytes() //snap, err := pm.raftStorage.CreateSnapshot(pm.appliedIndex, &pm.confState, snapData) - snapData := pm.buildSnapshot().toBytes(pm.useDns) + snapData := pm.buildSnapshot().toBytes() snap, err := pm.raftStorage.CreateSnapshot(index, &pm.confState, snapData) if err != nil { panic(err) @@ -206,32 +206,43 @@ func (pm *ProtocolManager) loadSnapshot() *raftpb.Snapshot { } } -func (snapshot *SnapshotWithHostnames) toBytes(useDns bool) []byte { - // we have DNS enabled, so only use the new snapshot type - if useDns { - buffer, err := rlp.EncodeToBytes(snapshot) - if err != nil { - panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error())) - } - return buffer - } +func (snapshot *SnapshotWithHostnames) toBytes() []byte { + var ( + useOldSnapshot bool + oldSnapshot SnapshotWithoutHostnames + toEncode interface{} + ) - // DNS is not enabled, use the old snapshot type, converting from hostnames to IP addresses - oldSnapshot := new(SnapshotWithoutHostnames) + // use old snapshot if all snapshot.Addresses are ips + // but use the new snapshot if any of it is a hostname + useOldSnapshot = true oldSnapshot.HeadBlockHash, oldSnapshot.RemovedRaftIds = snapshot.HeadBlockHash, snapshot.RemovedRaftIds oldSnapshot.Addresses = make([]AddressWithoutHostname, len(snapshot.Addresses)) for index, addrWithHost := range snapshot.Addresses { + // validate addrWithHost.Hostname is a hostname/ip + ip := net.ParseIP(addrWithHost.Hostname) + if ip == nil { + // this is a hostname + useOldSnapshot = false + break + } + // this is an ip oldSnapshot.Addresses[index] = AddressWithoutHostname{ addrWithHost.RaftId, addrWithHost.NodeId, - net.ParseIP(addrWithHost.Hostname), + ip, addrWithHost.P2pPort, addrWithHost.RaftPort, } } - buffer, err := rlp.EncodeToBytes(oldSnapshot) + if useOldSnapshot { + toEncode = oldSnapshot + } else { + toEncode = snapshot + } + buffer, err := rlp.EncodeToBytes(toEncode) if err != nil { panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error())) } @@ -247,6 +258,7 @@ func bytesToSnapshot(input []byte) *SnapshotWithHostnames { return snapshot } + // Build new snapshot with hostname from legacy Address struct snapshotOld := new(SnapshotWithoutHostnames) streamOldSnapshot := rlp.NewStream(bytes.NewReader(input), 0) if errOld = streamOldSnapshot.Decode(snapshotOld); errOld == nil { diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 32c18c597..66ae94a88 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -378,7 +378,7 @@ func RandomAddr() *BzzAddr { if err != nil { panic("unable to generate key") } - node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303, 0) + node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303) return NewAddr(node) } diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index 6fab4fd72..448749344 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -146,7 +146,7 @@ func initialize(t *testing.T) { peerNodeId := nodes[j].id address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr) peerPort := uint16(address.Port) - peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, int(peerPort), int(peerPort), 0) + peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, int(peerPort), int(peerPort)) node.server.AddPeer(peer) } diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go index 5ac53b9c4..65e62d96c 100644 --- a/whisper/whisperv6/peer_test.go +++ b/whisper/whisperv6/peer_test.go @@ -223,7 +223,7 @@ func initialize(t *testing.T) { for j := 0; j < i; j++ { peerNodeId := nodes[j].id address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr) - peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, address.Port, address.Port, 0) + peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, address.Port, address.Port) nodes[i].server.AddPeer(peer) } }