channeldb+graph/db: move net.Addr encode/decode to graph db package
In preparation for moving the graph related schema structs to the graph package in an upcoming commit, we move these methods to the graph package. The structs we will move make use of these methods but we still import them from channeldb so as to keep the ReadElement and WriteElement helpers working as they do today.
This commit is contained in:
8 files changed
+30
-14
No files matched your search
@@ -0,0 +1,221 @@
|
||||
package graphdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/lightningnetwork/lnd/tor"
|
||||
)
|
||||
|
||||
// addressType specifies the network protocol and version that should be used
|
||||
// when connecting to a node at a particular address.
|
||||
type addressType uint8
|
||||
|
||||
const (
|
||||
// tcp4Addr denotes an IPv4 TCP address.
|
||||
tcp4Addr addressType = 0
|
||||
|
||||
// tcp6Addr denotes an IPv6 TCP address.
|
||||
tcp6Addr addressType = 1
|
||||
|
||||
// v2OnionAddr denotes a version 2 Tor onion service address.
|
||||
v2OnionAddr addressType = 2
|
||||
|
||||
// v3OnionAddr denotes a version 3 Tor (prop224) onion service address.
|
||||
v3OnionAddr addressType = 3
|
||||
)
|
||||
|
||||
// encodeTCPAddr serializes a TCP address into its compact raw bytes
|
||||
// representation.
|
||||
func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error {
|
||||
var (
|
||||
addrType byte
|
||||
ip []byte
|
||||
)
|
||||
|
||||
if addr.IP.To4() != nil {
|
||||
addrType = byte(tcp4Addr)
|
||||
ip = addr.IP.To4()
|
||||
} else {
|
||||
addrType = byte(tcp6Addr)
|
||||
ip = addr.IP.To16()
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
return fmt.Errorf("unable to encode IP %v", addr.IP)
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte{addrType}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(ip); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var port [2]byte
|
||||
byteOrder.PutUint16(port[:], uint16(addr.Port))
|
||||
if _, err := w.Write(port[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeOnionAddr serializes an onion address into its compact raw bytes
|
||||
// representation.
|
||||
func encodeOnionAddr(w io.Writer, addr *tor.OnionAddr) error {
|
||||
var suffixIndex int
|
||||
hostLen := len(addr.OnionService)
|
||||
switch hostLen {
|
||||
case tor.V2Len:
|
||||
if _, err := w.Write([]byte{byte(v2OnionAddr)}); err != nil {
|
||||
return err
|
||||
}
|
||||
suffixIndex = tor.V2Len - tor.OnionSuffixLen
|
||||
case tor.V3Len:
|
||||
if _, err := w.Write([]byte{byte(v3OnionAddr)}); err != nil {
|
||||
return err
|
||||
}
|
||||
suffixIndex = tor.V3Len - tor.OnionSuffixLen
|
||||
default:
|
||||
return errors.New("unknown onion service length")
|
||||
}
|
||||
|
||||
suffix := addr.OnionService[suffixIndex:]
|
||||
if suffix != tor.OnionSuffix {
|
||||
return fmt.Errorf("invalid suffix \"%v\"", suffix)
|
||||
}
|
||||
|
||||
host, err := tor.Base32Encoding.DecodeString(
|
||||
addr.OnionService[:suffixIndex],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanity check the decoded length.
|
||||
switch {
|
||||
case hostLen == tor.V2Len && len(host) != tor.V2DecodedLen:
|
||||
return fmt.Errorf("onion service %v decoded to invalid host %x",
|
||||
addr.OnionService, host)
|
||||
|
||||
case hostLen == tor.V3Len && len(host) != tor.V3DecodedLen:
|
||||
return fmt.Errorf("onion service %v decoded to invalid host %x",
|
||||
addr.OnionService, host)
|
||||
}
|
||||
|
||||
if _, err := w.Write(host); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var port [2]byte
|
||||
byteOrder.PutUint16(port[:], uint16(addr.Port))
|
||||
if _, err := w.Write(port[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeserializeAddr reads the serialized raw representation of an address and
|
||||
// deserializes it into the actual address. This allows us to avoid address
|
||||
// resolution within the channeldb package.
|
||||
func DeserializeAddr(r io.Reader) (net.Addr, error) {
|
||||
var addrType [1]byte
|
||||
if _, err := r.Read(addrType[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var address net.Addr
|
||||
switch addressType(addrType[0]) {
|
||||
case tcp4Addr:
|
||||
var ip [4]byte
|
||||
if _, err := r.Read(ip[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var port [2]byte
|
||||
if _, err := r.Read(port[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address = &net.TCPAddr{
|
||||
IP: net.IP(ip[:]),
|
||||
Port: int(binary.BigEndian.Uint16(port[:])),
|
||||
}
|
||||
case tcp6Addr:
|
||||
var ip [16]byte
|
||||
if _, err := r.Read(ip[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var port [2]byte
|
||||
if _, err := r.Read(port[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address = &net.TCPAddr{
|
||||
IP: net.IP(ip[:]),
|
||||
Port: int(binary.BigEndian.Uint16(port[:])),
|
||||
}
|
||||
case v2OnionAddr:
|
||||
var h [tor.V2DecodedLen]byte
|
||||
if _, err := r.Read(h[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p [2]byte
|
||||
if _, err := r.Read(p[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
onionService := tor.Base32Encoding.EncodeToString(h[:])
|
||||
onionService += tor.OnionSuffix
|
||||
port := int(binary.BigEndian.Uint16(p[:]))
|
||||
|
||||
address = &tor.OnionAddr{
|
||||
OnionService: onionService,
|
||||
Port: port,
|
||||
}
|
||||
case v3OnionAddr:
|
||||
var h [tor.V3DecodedLen]byte
|
||||
if _, err := r.Read(h[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p [2]byte
|
||||
if _, err := r.Read(p[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
onionService := tor.Base32Encoding.EncodeToString(h[:])
|
||||
onionService += tor.OnionSuffix
|
||||
port := int(binary.BigEndian.Uint16(p[:]))
|
||||
|
||||
address = &tor.OnionAddr{
|
||||
OnionService: onionService,
|
||||
Port: port,
|
||||
}
|
||||
default:
|
||||
return nil, ErrUnknownAddressType
|
||||
}
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
// SerializeAddr serializes an address into its raw bytes representation so that
|
||||
// it can be deserialized without requiring address resolution.
|
||||
func SerializeAddr(w io.Writer, address net.Addr) error {
|
||||
switch addr := address.(type) {
|
||||
case *net.TCPAddr:
|
||||
return encodeTCPAddr(w, addr)
|
||||
case *tor.OnionAddr:
|
||||
return encodeOnionAddr(w, addr)
|
||||
default:
|
||||
return ErrUnknownAddressType
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package graphdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/tor"
|
||||
)
|
||||
|
||||
type unknownAddrType struct{}
|
||||
|
||||
func (t unknownAddrType) Network() string { return "unknown" }
|
||||
func (t unknownAddrType) String() string { return "unknown" }
|
||||
|
||||
var testIP4 = net.ParseIP("192.168.1.1")
|
||||
var testIP6 = net.ParseIP("2001:0db8:0000:0000:0000:ff00:0042:8329")
|
||||
|
||||
var addrTests = []struct {
|
||||
expAddr net.Addr
|
||||
serErr string
|
||||
}{
|
||||
// Valid addresses.
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
IP: testIP4,
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
IP: testIP6,
|
||||
Port: 65535,
|
||||
},
|
||||
},
|
||||
{
|
||||
expAddr: &tor.OnionAddr{
|
||||
OnionService: "3g2upl4pq6kufc4m.onion",
|
||||
Port: 9735,
|
||||
},
|
||||
},
|
||||
{
|
||||
expAddr: &tor.OnionAddr{
|
||||
OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
|
||||
// Invalid addresses.
|
||||
{
|
||||
expAddr: unknownAddrType{},
|
||||
serErr: ErrUnknownAddressType.Error(),
|
||||
},
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
// Remove last byte of IPv4 address.
|
||||
IP: testIP4[:len(testIP4)-1],
|
||||
Port: 12345,
|
||||
},
|
||||
serErr: "unable to encode",
|
||||
},
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
// Add an extra byte of IPv4 address.
|
||||
IP: append(testIP4, 0xff),
|
||||
Port: 12345,
|
||||
},
|
||||
serErr: "unable to encode",
|
||||
},
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
// Remove last byte of IPv6 address.
|
||||
IP: testIP6[:len(testIP6)-1],
|
||||
Port: 65535,
|
||||
},
|
||||
serErr: "unable to encode",
|
||||
},
|
||||
{
|
||||
expAddr: &net.TCPAddr{
|
||||
// Add an extra byte to the IPv6 address.
|
||||
IP: append(testIP6, 0xff),
|
||||
Port: 65535,
|
||||
},
|
||||
serErr: "unable to encode",
|
||||
},
|
||||
{
|
||||
expAddr: &tor.OnionAddr{
|
||||
// Invalid suffix.
|
||||
OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.inion",
|
||||
Port: 80,
|
||||
},
|
||||
serErr: "invalid suffix",
|
||||
},
|
||||
{
|
||||
expAddr: &tor.OnionAddr{
|
||||
// Invalid length.
|
||||
OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyy.onion",
|
||||
Port: 80,
|
||||
},
|
||||
serErr: "unknown onion service length",
|
||||
},
|
||||
{
|
||||
expAddr: &tor.OnionAddr{
|
||||
// Invalid encoding.
|
||||
OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyA.onion",
|
||||
Port: 80,
|
||||
},
|
||||
serErr: "illegal base32",
|
||||
},
|
||||
}
|
||||
|
||||
// TestAddrSerialization tests that the serialization method used by channeldb
|
||||
// for net.Addr's works as intended.
|
||||
func TestAddrSerialization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var b bytes.Buffer
|
||||
for _, test := range addrTests {
|
||||
err := SerializeAddr(&b, test.expAddr)
|
||||
switch {
|
||||
case err == nil && test.serErr != "":
|
||||
t.Fatalf("expected serialization err for addr %v",
|
||||
test.expAddr)
|
||||
|
||||
case err != nil && test.serErr == "":
|
||||
t.Fatalf("unexpected serialization err for addr %v: %v",
|
||||
test.expAddr, err)
|
||||
|
||||
case err != nil && !strings.Contains(err.Error(), test.serErr):
|
||||
t.Fatalf("unexpected serialization err for addr %v, "+
|
||||
"want: %v, got %v", test.expAddr, test.serErr,
|
||||
err)
|
||||
|
||||
case err != nil:
|
||||
continue
|
||||
}
|
||||
|
||||
addr, err := DeserializeAddr(&b)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to deserialize address: %v", err)
|
||||
}
|
||||
|
||||
if addr.String() != test.expAddr.String() {
|
||||
t.Fatalf("expected address %v after serialization, "+
|
||||
"got %v", addr, test.expAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package graphdb
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
var (
|
||||
// byteOrder defines the preferred byte order, which is Big Endian.
|
||||
byteOrder = binary.BigEndian
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package graphdb
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
// ErrUnknownAddressType is returned when a node's addressType is not
|
||||
// an expected value.
|
||||
ErrUnknownAddressType = fmt.Errorf("address type cannot be resolved")
|
||||
)
|
||||
Reference in new issue
Block a user