From f46335ac0f073d42139b54f4295372b63aad8cfb Mon Sep 17 00:00:00 2001 From: delbonis Date: Wed, 21 Nov 2018 15:11:04 -0500 Subject: [PATCH 1/5] Added important parts of new address format code. --- lncore/addr.go | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 lncore/addr.go diff --git a/lncore/addr.go b/lncore/addr.go new file mode 100644 index 000000000..4ea938cb4 --- /dev/null +++ b/lncore/addr.go @@ -0,0 +1,145 @@ +package lncore; + +import ( + "fmt" + + "github.com/mit-dci/lit/bech32" +) + +/* + * Ok so there's a lot of stuff going on here. Lit natively supports addresses + * that are just the pkh of the pubkey encoded in bech32. And that's pretty + * cool, you look up the IP+port in a tracker (or DHT) and connected with + * Noise_XX, then verify that the pubkey they provide you matches the pkh you + * already have. Also you can provide IP+port if you want. + * + * But in mainstream BOLT nodes they use the full pubkey hex-encoded. They have + * a DHT where you look up the IP+port (if not provided) and connect with + * Noise_XK, which assumes you already have the pubkey (since you do) and that's + * all fine. + * + * This tries to unify all of them with formats like this under 1 parser: + * + * [@[:] + * or + * + * [@[:] + * + * or fully + * + * :[@:] + */ + +// LnDefaultPort is from BOLT1. +const LnDefaultPort = 9735 + +// PkhBech32Prefix if for addresses lit already uses. +const PkhBech32Prefix = "ln" + +// PubkeyBech32Prefix is for encoding the full pubkey in bech32. +const PubkeyBech32Prefix = "lnpk" + +// LnAddressData is all of the data that can be encoded in a parsed address. +type LnAddressData struct { + Pkh *string + Pubkey []byte + IPAddr *string + Port uint16 +} + +// AddrFmtFull is :@: +const AddrFmtFull = "full" + +// AddrFmtFullBech32Pk is :@: +const AddrFmtFullBech32Pk = "full_bech32_pk" + +// AddrFmtFullNoPort is :@ +const AddrFmtFullNoPort = "full_no_port" + +// AddrFmtFullBech32PkNoPort is :@ +const AddrFmtFullBech32PkNoPort = "full_bech32_pk_no_port" + +// AddrFmtBech32 is just +const AddrFmtBech32 = "bech32_pkh" + +// AddrFmtLit is just AddrFmtBech32 +const AddrFmtLit = "lit" + +// AddrFmtPubkey is +const AddrFmtPubkey = "hex_pk" + +// AddrFmtBech32Pubkey is +const AddrFmtBech32Pubkey = "bech32_pk" + +// AddrFmtPubkeyIP is @ +const AddrFmtPubkeyIP = "hex_pk_ip" + +// AddrFmtPubkeyIPPort is @: +const AddrFmtPubkeyIPPort = "hex_pk_ip_port" + +// AddrFmtLitFull is @: +const AddrFmtLitFull = "lit_full" + +// AddrFmtLitIP is @ +const AddrFmtLitIP = "lit_ip" + +// AddrDataToFormats returns the addresses in all of the formats that fully represent it. +func DumpAddressFormats(data LnAddressData) (map[string]string, error) { + + ret := make(map[string]string) + + if data.Port != LnDefaultPort && data.IPAddr != nil { + return ret, fmt.Errorf("nondefault port specified but IP not specified") + } + + // Full. + if data.Pkh != nil && data.Pubkey != nil && data.IPAddr != nil { + ret[AddrFmtFull] = fmt.Sprintf("%s:%x@%s:%d", *data.Pkh, data.Pubkey, *data.IPAddr, data.Port) + ret[AddrFmtFullBech32Pk] = fmt.Sprintf("%s:%s@%s:%d", *data.Pkh, bech32.Encode(PubkeyBech32Prefix, data.Pubkey), *data.IPAddr, data.Port) + } + + // Minimal Lit. + if data.Pkh != nil { + ret[AddrFmtLit] = *data.Pkh + } + + // Long Lit. + if data.Pkh != nil && data.IPAddr != nil { + if data.Port == LnDefaultPort { + ret[AddrFmtLitIP] = fmt.Sprintf("%s@%s", *data.Pkh, *data.IPAddr) + } + ret[AddrFmtLitFull] = fmt.Sprintf("%s@%s:%d", *data.Pkh, *data.IPAddr, data.Port) + } + + // Hex pubkey stuff + if data.Pubkey != nil { + ret[AddrFmtPubkey] = fmt.Sprintf("%x", data.Pubkey) + if data.IPAddr != nil { + if data.Port == LnDefaultPort { + ret[AddrFmtPubkeyIP] = fmt.Sprintf("%x@%s", data.Pubkey, *data.IPAddr) + } + ret[AddrFmtPubkeyIPPort] = fmt.Sprintf("%x@%s:%d", data.Pubkey, *data.IPAddr, data.Port) + } + } + + // TODO more + + return ret, nil + +} + +/* ++0 q p z r y 9 x 8 ++8 g f 2 t v d w 0 ++16 s 3 j n 5 4 k h ++24 c e 6 m u a 7 l +*/ + +const bech32bodyregex = `[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{4,}` +const ipaddrregex = `[0-9]{1,3}(\.[0-9]{1,3}){3}` +const fulladdrregex = `ln1` + bech32bodyregex + `(:([0-9a-fA-F]{64}|lnpk1` + bech32bodyregex + `))?(@` + ipaddrregex + `(:[0-9]{,5})?)?` + +// ParseLnAddrData takes a LnAddr and parses the internal data. +func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { + return nil, nil // TODO +} From e6969f94c165e8a35991c2e5ee6ab6c1738dc8ba Mon Sep 17 00:00:00 2001 From: delbonis Date: Tue, 27 Nov 2018 11:23:33 -0500 Subject: [PATCH 2/5] Added a function to check if we need to do a lookup to find the connection address, plus docs. --- lncore/addr.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lncore/addr.go b/lncore/addr.go index 4ea938cb4..6c3d8d3e0 100644 --- a/lncore/addr.go +++ b/lncore/addr.go @@ -47,6 +47,13 @@ type LnAddressData struct { Port uint16 } +// HasFullAddress returns whether or not we need to do a tracker/DHT lookup in +// order to resolve the connection address for this address data. Basically, it +// checks if there's an IP address defined. +func (d *LnAddressData) HasFullAddress() bool { + return d.IPAddr != nil +} + // AddrFmtFull is :@: const AddrFmtFull = "full" @@ -83,7 +90,7 @@ const AddrFmtLitFull = "lit_full" // AddrFmtLitIP is @ const AddrFmtLitIP = "lit_ip" -// AddrDataToFormats returns the addresses in all of the formats that fully represent it. +// DumpAddressFormats returns the addresses in all of the formats that fully represent it. func DumpAddressFormats(data LnAddressData) (map[string]string, error) { ret := make(map[string]string) From ec19e2df0c226e8da167237820b4dda04f78507d Mon Sep 17 00:00:00 2001 From: delbonis Date: Tue, 27 Nov 2018 12:43:33 -0500 Subject: [PATCH 3/5] Added most of address parsing functions. --- lncore/addr.go | 170 +++++++++++++++++++++++++++++++++++++++++--- lncore/addr_test.go | 44 ++++++++++++ lncore/peers.go | 41 ----------- 3 files changed, 204 insertions(+), 51 deletions(-) create mode 100644 lncore/addr_test.go diff --git a/lncore/addr.go b/lncore/addr.go index 6c3d8d3e0..acbc3d400 100644 --- a/lncore/addr.go +++ b/lncore/addr.go @@ -1,8 +1,12 @@ -package lncore; +package lncore import ( "fmt" - + "regexp" + "strings" + "strconv" + "encoding/hex" + "github.com/mit-dci/lit/bech32" ) @@ -22,7 +26,7 @@ import ( * * [@[:] * or - * + * * [@[:] * * or fully @@ -30,20 +34,37 @@ import ( * :[@:] */ +// LnAddr is just a bech32-encoded pubkey. +// TODO Move this to another package so it's more obviously not *just* IO-related. +type LnAddr string + +// ToString returns the LnAddr as a string. Right now it just unwraps it but it +// might do something more eventually. +func (lnaddr LnAddr) ToString() string { + return string(lnaddr) +} + // LnDefaultPort is from BOLT1. const LnDefaultPort = 9735 // PkhBech32Prefix if for addresses lit already uses. -const PkhBech32Prefix = "ln" +const PkhBech32Prefix = "ln1" // PubkeyBech32Prefix is for encoding the full pubkey in bech32. -const PubkeyBech32Prefix = "lnpk" +const PubkeyBech32Prefix = "lnpk1" // LnAddressData is all of the data that can be encoded in a parsed address. type LnAddressData struct { + // Pkh is the pubkey hash as bech32. Pkh *string + + // Pubkey is the pubkey, as bytes, decoded from source format. Pubkey []byte + + // IPAddr is the IP address, if present. IPAddr *string + + // Port should always be defined, default is LnDefaultPort. Port uint16 } @@ -128,11 +149,11 @@ func DumpAddressFormats(data LnAddressData) (map[string]string, error) { ret[AddrFmtPubkeyIPPort] = fmt.Sprintf("%x@%s:%d", data.Pubkey, *data.IPAddr, data.Port) } } - + // TODO more - + return ret, nil - + } /* @@ -146,7 +167,136 @@ const bech32bodyregex = `[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{4,}` const ipaddrregex = `[0-9]{1,3}(\.[0-9]{1,3}){3}` const fulladdrregex = `ln1` + bech32bodyregex + `(:([0-9a-fA-F]{64}|lnpk1` + bech32bodyregex + `))?(@` + ipaddrregex + `(:[0-9]{,5})?)?` -// ParseLnAddrData takes a LnAddr and parses the internal data. +// ParseLnAddrData takes a LnAddr and parses the internal data. Assumes it's +// not been force-casted as full checks are done in the ParseLnAddr function. func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { - return nil, nil // TODO + + addrdata := new(LnAddressData) + + parts := strings.SplitN(string(addr), "@", 2) // using cast here because reasons + + // First process the identify information. + pkdata := strings.SplitN(parts[0], ":", 2) + if len(pkdata) == 2 { + + // The pkh is already in the right format for internal storage. + addrdata.Pkh = &pkdata[0] + + // Now figure out how to parse the full pubkey info. + if strings.HasPrefix(pkdata[1], PubkeyBech32Prefix) { + _, data, err := bech32.Decode(pkdata[1]) + if err != nil { + return nil, err + } + addrdata.Pubkey = data + } else { + data, err := hex.DecodeString(pkdata[1]) + if err != nil { + return nil, err + } + addrdata.Pubkey = data + } + + } else { + + // If there's only 1 part then there's 3 mutually exclusive options. + if strings.HasPrefix(pkdata[0], PkhBech32Prefix) { + addrdata.Pkh = &pkdata[0] + } else if strings.HasPrefix(pkdata[0], PubkeyBech32Prefix) { + _, data, err := bech32.Decode(pkdata[0]) + if err != nil { + return nil, err + } + addrdata.Pubkey = data + } else { + data, err := hex.DecodeString(pkdata[0]) + if err != nil { + return nil, err + } + addrdata.Pubkey = data + } + + } + + // Now parse the location information if it's there. + if len(parts) > 1 { + ipdata := strings.SplitN(parts[1], ":", 2) + + // The IP address is already in the right format, probably. + addrdata.IPAddr = &ipdata[0] + + // Parse the port if it's there. + if len(ipdata) > 1 { + port, err := strconv.Atoi(ipdata[1]) + if err != nil { + return nil, err + } + if port > 65535 { + return nil, fmt.Errorf("port number %d not in range", port) + } + addrdata.Port = uint16(port) + } else { + addrdata.Port = LnDefaultPort + } + + } + + return addrdata, nil // TODO +} + +// ParseLnAddr will verify that the string passed is a valid LN address of any format. +func ParseLnAddr(m string) (LnAddr, error) { + + r := regexp.MustCompile(fulladdrregex) + + if !r.Match([]byte(m)) { + return "", fmt.Errorf("address doesn't match overall format") + } + + parts := strings.SplitN(m, "@", 2) + pkdata := strings.SplitN(parts[0], ":", 2) + if len(pkdata) == 2 { + // This means there's both a pkh and a pubkey. + + pkhprefix, _, pkherr := bech32.Decode(pkdata[0]) + if pkherr != nil { + return "", fmt.Errorf("invalid pkh bech32") + } + if pkhprefix + "1" != PkhBech32Prefix { + return "", fmt.Errorf("pkh bech32 prefix incorrect") + } + + pkprefix, _, pkerr := bech32.Decode(pkdata[1]) + if pkerr != nil { + // Maybe it's hex. + _, err := hex.DecodeString(pkdata[1]) + if err != nil { + return "", fmt.Errorf("pubkey not valid bech32 or hex") + } + } + if pkprefix + "1" != PubkeyBech32Prefix { + return "", fmt.Errorf("pubkey bech32 prefix incorrect") + } + + } else { + // This means there's *either* a pubkey or pkh, in some format. + + prefix, _, err := bech32.Decode(pkdata[0]) + if err != nil { + // Maybe it's hex. + _, err := hex.DecodeString(pkdata[0]) + if err != nil { + return "", fmt.Errorf("pubkey not valid bech32 or hex") + } + } + + if err == nil && prefix + "1" != PkhBech32Prefix && prefix + "1" != PubkeyBech32Prefix { + return "", fmt.Errorf("pubkey (or phk) bech32 prefix incorrect") + } + } + + // TODO Parse the IP address information, I guess. + + return LnAddr(m), nil // should be the only place we cast to this type + } diff --git a/lncore/addr_test.go b/lncore/addr_test.go new file mode 100644 index 000000000..ff0a93584 --- /dev/null +++ b/lncore/addr_test.go @@ -0,0 +1,44 @@ +package lncore + +import ( + "fmt" + "encoding/json" + "testing" +) + +var addrs = []string{ + "ln1acdef", + "ln1pmclh89haeswrw0unf8awuyqeu4t2uell58ne@1.2.3.4:12345", +} + +func TestAddrVerify(t *testing.T) { + for _, a := range addrs { + _, err := ParseLnAddr(a) + if err != nil { + fmt.Printf("didn't parse but should have: %s (%s)\n", a, err.Error()) + t.Fail() + } + } +} + +var notAddrs = []string{ + "asdfasdfasdfasfdadfs", +} + +func TestAddrVerifyFail(t *testing.T) { + for _, a := range notAddrs { + _, err := ParseLnAddr(a) + if err == nil { + fmt.Printf("parsed but should not have: %s\n", a) + t.Fail() + } + } +} + +func TestAddrParse(t *testing.T) { + if false { + t.FailNow() + } + + _, _ = json.Marshal(t) +} diff --git a/lncore/peers.go b/lncore/peers.go index 567c84f45..479e65238 100644 --- a/lncore/peers.go +++ b/lncore/peers.go @@ -1,46 +1,5 @@ package lncore -import ( - "fmt" - - "github.com/mit-dci/lit/bech32" -) - -// LnAddr is just a bech32-encoded pubkey. -// TODO Move this to another package so it's more obviously not *just* IO-related. -type LnAddr string - -// ParseLnAddr will verify that the string passed is a valid LN address, as in -// ln1pmclh89haeswrw0unf8awuyqeu4t2uell58nea. -func ParseLnAddr(m string) (LnAddr, error) { - - prefix, raw, err := bech32.Decode(m) - - // Check it's valid bech32. - if err != nil { - return "", err - } - - // Check it has the right prefix. - if prefix != "ln" { - return "", fmt.Errorf("prefix is not 'ln'") - } - - // Check the length of the content bytes is right. - if len(raw) > 20 { - return "", fmt.Errorf("address too long to be pubkey") - } - - return LnAddr(m), nil // should be the only place we cast to this type - -} - -// ToString returns the LnAddr as a string. Right now it just unwraps it but it -// might do something more eventually. -func (lnaddr LnAddr) ToString() string { - return string(lnaddr) -} - // LitPeerStorage is storage for peer data. type LitPeerStorage interface { GetPeerAddrs() ([]LnAddr, error) From fcc5d0b17b132ff8fb2504d344fd1abec99fed78 Mon Sep 17 00:00:00 2001 From: delbonis Date: Tue, 27 Nov 2018 15:46:20 -0500 Subject: [PATCH 4/5] Added some more testing code and did some cleanup. --- lncore/addr.go | 81 +++++++++++++++++++++++++++------------------ lncore/addr_test.go | 66 ++++++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 38 deletions(-) diff --git a/lncore/addr.go b/lncore/addr.go index acbc3d400..c6ab307c5 100644 --- a/lncore/addr.go +++ b/lncore/addr.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "github.com/mit-dci/lit/bech32" + "github.com/mit-dci/lit/crypto/fastsha256" ) /* @@ -48,10 +49,10 @@ func (lnaddr LnAddr) ToString() string { const LnDefaultPort = 9735 // PkhBech32Prefix if for addresses lit already uses. -const PkhBech32Prefix = "ln1" +const PkhBech32Prefix = "ln" // PubkeyBech32Prefix is for encoding the full pubkey in bech32. -const PubkeyBech32Prefix = "lnpk1" +const PubkeyBech32Prefix = "lnpk" // LnAddressData is all of the data that can be encoded in a parsed address. type LnAddressData struct { @@ -112,20 +113,20 @@ const AddrFmtLitFull = "lit_full" const AddrFmtLitIP = "lit_ip" // DumpAddressFormats returns the addresses in all of the formats that fully represent it. -func DumpAddressFormats(data LnAddressData) (map[string]string, error) { +func DumpAddressFormats(data *LnAddressData) (map[string]string, error) { ret := make(map[string]string) - if data.Port != LnDefaultPort && data.IPAddr != nil { + if data.Port != LnDefaultPort && data.IPAddr == nil { return ret, fmt.Errorf("nondefault port specified but IP not specified") } - // Full. + // Full. if data.Pkh != nil && data.Pubkey != nil && data.IPAddr != nil { - ret[AddrFmtFull] = fmt.Sprintf("%s:%x@%s:%d", *data.Pkh, data.Pubkey, *data.IPAddr, data.Port) + ret[AddrFmtFull] = fmt.Sprintf("%s:%X@%s:%d", *data.Pkh, data.Pubkey, *data.IPAddr, data.Port) ret[AddrFmtFullBech32Pk] = fmt.Sprintf("%s:%s@%s:%d", *data.Pkh, bech32.Encode(PubkeyBech32Prefix, data.Pubkey), *data.IPAddr, data.Port) } - + // Minimal Lit. if data.Pkh != nil { ret[AddrFmtLit] = *data.Pkh @@ -141,12 +142,12 @@ func DumpAddressFormats(data LnAddressData) (map[string]string, error) { // Hex pubkey stuff if data.Pubkey != nil { - ret[AddrFmtPubkey] = fmt.Sprintf("%x", data.Pubkey) + ret[AddrFmtPubkey] = fmt.Sprintf("%X", data.Pubkey) if data.IPAddr != nil { if data.Port == LnDefaultPort { - ret[AddrFmtPubkeyIP] = fmt.Sprintf("%x@%s", data.Pubkey, *data.IPAddr) + ret[AddrFmtPubkeyIP] = fmt.Sprintf("%X@%s", data.Pubkey, *data.IPAddr) } - ret[AddrFmtPubkeyIPPort] = fmt.Sprintf("%x@%s:%d", data.Pubkey, *data.IPAddr, data.Port) + ret[AddrFmtPubkeyIPPort] = fmt.Sprintf("%X@%s:%d", data.Pubkey, *data.IPAddr, data.Port) } } @@ -156,17 +157,6 @@ func DumpAddressFormats(data LnAddressData) (map[string]string, error) { } -/* -+0 q p z r y 9 x 8 -+8 g f 2 t v d w 0 -+16 s 3 j n 5 4 k h -+24 c e 6 m u a 7 l -*/ - -const bech32bodyregex = `[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{4,}` -const ipaddrregex = `[0-9]{1,3}(\.[0-9]{1,3}){3}` -const fulladdrregex = `ln1` + bech32bodyregex + `(:([0-9a-fA-F]{64}|lnpk1` + bech32bodyregex + `))?(@` + ipaddrregex + `(:[0-9]{,5})?)?` - // ParseLnAddrData takes a LnAddr and parses the internal data. Assumes it's // not been force-casted as full checks are done in the ParseLnAddr function. func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { @@ -183,7 +173,7 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { addrdata.Pkh = &pkdata[0] // Now figure out how to parse the full pubkey info. - if strings.HasPrefix(pkdata[1], PubkeyBech32Prefix) { + if strings.HasPrefix(pkdata[1], PubkeyBech32Prefix + "1") { _, data, err := bech32.Decode(pkdata[1]) if err != nil { return nil, err @@ -200,9 +190,9 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { } else { // If there's only 1 part then there's 3 mutually exclusive options. - if strings.HasPrefix(pkdata[0], PkhBech32Prefix) { + if strings.HasPrefix(pkdata[0], PkhBech32Prefix + "1") { addrdata.Pkh = &pkdata[0] - } else if strings.HasPrefix(pkdata[0], PubkeyBech32Prefix) { + } else if strings.HasPrefix(pkdata[0], PubkeyBech32Prefix + "1") { _, data, err := bech32.Decode(pkdata[0]) if err != nil { return nil, err @@ -215,6 +205,12 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { } addrdata.Pubkey = data } + + // Now we figure out what the pkh should be if the full pubkey is specified. + if addrdata.Pkh == nil && addrdata.Pubkey != nil { + pkh := ConvertPubkeyToBech32Pkh(addrdata.Pubkey) + addrdata.Pkh = &pkh + } } @@ -244,6 +240,19 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { return addrdata, nil // TODO } +/* ++0 q p z r y 9 x 8 ++8 g f 2 t v d w 0 ++16 s 3 j n 5 4 k h ++24 c e 6 m u a 7 l +*/ + +const bech32bodyregex = `[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{4,}` +const ipaddrregex = `[0-9]{1,3}(\.[0-9]{1,3}){3}` +const pkhregex = `ln1` + bech32bodyregex +const pubkeyregex = `(lnpk1` + bech32bodyregex + `|` + `[0-9a-fA-F]{64})` +const fulladdrregex = `(` + pkhregex + `|` + pubkeyregex + `|` + pkhregex + `:` + pubkeyregex + `)(@` + ipaddrregex + `(:[0-9]{1,5})?)?` + // ParseLnAddr will verify that the string passed is a valid LN address of any format. func ParseLnAddr(m string) (LnAddr, error) { @@ -262,7 +271,7 @@ func ParseLnAddr(m string) (LnAddr, error) { if pkherr != nil { return "", fmt.Errorf("invalid pkh bech32") } - if pkhprefix + "1" != PkhBech32Prefix { + if pkhprefix != PkhBech32Prefix { return "", fmt.Errorf("pkh bech32 prefix incorrect") } @@ -271,10 +280,10 @@ func ParseLnAddr(m string) (LnAddr, error) { // Maybe it's hex. _, err := hex.DecodeString(pkdata[1]) if err != nil { - return "", fmt.Errorf("pubkey not valid bech32 or hex") + return "", fmt.Errorf("pubkey not valid bech32 or hex ('%s' and '%s')", pkerr.Error(), err.Error()) } } - if pkprefix + "1" != PubkeyBech32Prefix { + if pkprefix != PubkeyBech32Prefix { return "", fmt.Errorf("pubkey bech32 prefix incorrect") } @@ -284,19 +293,27 @@ func ParseLnAddr(m string) (LnAddr, error) { prefix, _, err := bech32.Decode(pkdata[0]) if err != nil { // Maybe it's hex. - _, err := hex.DecodeString(pkdata[0]) - if err != nil { - return "", fmt.Errorf("pubkey not valid bech32 or hex") + _, err2 := hex.DecodeString(pkdata[0]) + if err2 != nil { + return "", fmt.Errorf("pubkey not valid bech32 or hex ('%s' and '%s')", err.Error(), err2.Error()) } } - if err == nil && prefix + "1" != PkhBech32Prefix && prefix + "1" != PubkeyBech32Prefix { + if err == nil && prefix != PkhBech32Prefix && prefix != PubkeyBech32Prefix { return "", fmt.Errorf("pubkey (or phk) bech32 prefix incorrect") } } - // TODO Parse the IP address information, I guess. + // Should we make sure that the pubkey and pkh match if both are present? return LnAddr(m), nil // should be the only place we cast to this type } + +// ConvertPubkeyToBech32Pkh converts the pubkey into the bech32 representation +// of the PKH. +func ConvertPubkeyToBech32Pkh(pubkey []byte) string { + hash := fastsha256.Sum256(pubkey) + enc := bech32.Encode("ln", hash[:20]) + return enc +} diff --git a/lncore/addr_test.go b/lncore/addr_test.go index ff0a93584..7cf2a8b0c 100644 --- a/lncore/addr_test.go +++ b/lncore/addr_test.go @@ -3,12 +3,16 @@ package lncore import ( "fmt" "encoding/json" + "math/rand" "testing" + + //"github.com/mit-dci/lit/bech32" ) var addrs = []string{ - "ln1acdef", - "ln1pmclh89haeswrw0unf8awuyqeu4t2uell58ne@1.2.3.4:12345", + "ln1jcrlgzng25kxqgz5n5m0xeu5960dl020cnefwk", + "ln1095qlqa8y3jlav7unuds5twwduucwzvcmj4xnu@1.2.3.4:12345", + "lnpk1jchkczu7p6g6q3ykcp90rgwd5rfveqc9u03r7325azdcyq89kqdqd0u873@1.2.3.4:12345", } func TestAddrVerify(t *testing.T) { @@ -35,10 +39,60 @@ func TestAddrVerifyFail(t *testing.T) { } } -func TestAddrParse(t *testing.T) { - if false { - t.FailNow() +func makeRandomAddressData(seed int64) *LnAddressData { + + r := rand.New(rand.NewSource(seed)) + + ad := &LnAddressData{ + Pkh: nil, + Pubkey: nil, + IPAddr: nil, + Port: LnDefaultPort, } - _, _ = json.Marshal(t) + mkpkhnopk := r.Int() % 4 == 0 + mkipa := r.Int() % 2 == 0 + mkuport := mkipa && r.Int() % 2 == 0 + + pkbuf := [32]byte{} + r.Read(pkbuf[:]) + + pkhbech32 := ConvertPubkeyToBech32Pkh(pkbuf[:]) + ad.Pkh = &pkhbech32 + + if !mkpkhnopk { + ad.Pubkey = pkbuf[:] + } + + if mkipa { + s := fmt.Sprintf("%d.%d.%d.%d", r.Intn(256), r.Intn(256), r.Intn(256), r.Intn(256)) + ad.IPAddr = &s + } + + if mkipa && mkuport { + ad.Port = uint16(r.Intn(10000) + 1024) + } + + return ad +} + +func TestGenAddresses(t *testing.T) { + for i := 0; i < 4; i++ { + raddr := makeRandomAddressData(int64(i + 3)) // +3 so it'll generate all the variations we want + j, err := json.Marshal(raddr) + if err != nil { + fmt.Printf("address jsonify error: %s\n", err.Error()) + t.FailNow() + } + fmt.Printf("-----\n%s\n", j) + fmts, err := DumpAddressFormats(raddr) + if err != nil { + fmt.Printf("address dump error: %s\n", err.Error()) + t.FailNow() + } + for k, v := range fmts { + fmt.Printf("%s :\n\t%s\n", k, v) + } + fmt.Println("") + } } From b839468b5cd429089a8ac4a07e2dc8a7cb3af2af Mon Sep 17 00:00:00 2001 From: delbonis Date: Mon, 3 Dec 2018 11:39:26 -0500 Subject: [PATCH 5/5] Changed some names of consts. --- lncore/addr.go | 24 ++++++++++++------------ lncore/addr_test.go | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lncore/addr.go b/lncore/addr.go index c6ab307c5..408e78be0 100644 --- a/lncore/addr.go +++ b/lncore/addr.go @@ -45,11 +45,11 @@ func (lnaddr LnAddr) ToString() string { return string(lnaddr) } -// LnDefaultPort is from BOLT1. -const LnDefaultPort = 9735 +// DefaultPort is from BOLT1. +const DefaultPort = 9735 -// PkhBech32Prefix if for addresses lit already uses. -const PkhBech32Prefix = "ln" +// LitBech32Prefix if for addresses lit already uses. +const LitBech32Prefix = "ln" // PubkeyBech32Prefix is for encoding the full pubkey in bech32. const PubkeyBech32Prefix = "lnpk" @@ -65,7 +65,7 @@ type LnAddressData struct { // IPAddr is the IP address, if present. IPAddr *string - // Port should always be defined, default is LnDefaultPort. + // Port should always be defined, default is DefaultPort. Port uint16 } @@ -117,7 +117,7 @@ func DumpAddressFormats(data *LnAddressData) (map[string]string, error) { ret := make(map[string]string) - if data.Port != LnDefaultPort && data.IPAddr == nil { + if data.Port != DefaultPort && data.IPAddr == nil { return ret, fmt.Errorf("nondefault port specified but IP not specified") } @@ -134,7 +134,7 @@ func DumpAddressFormats(data *LnAddressData) (map[string]string, error) { // Long Lit. if data.Pkh != nil && data.IPAddr != nil { - if data.Port == LnDefaultPort { + if data.Port == DefaultPort { ret[AddrFmtLitIP] = fmt.Sprintf("%s@%s", *data.Pkh, *data.IPAddr) } ret[AddrFmtLitFull] = fmt.Sprintf("%s@%s:%d", *data.Pkh, *data.IPAddr, data.Port) @@ -144,7 +144,7 @@ func DumpAddressFormats(data *LnAddressData) (map[string]string, error) { if data.Pubkey != nil { ret[AddrFmtPubkey] = fmt.Sprintf("%X", data.Pubkey) if data.IPAddr != nil { - if data.Port == LnDefaultPort { + if data.Port == DefaultPort { ret[AddrFmtPubkeyIP] = fmt.Sprintf("%X@%s", data.Pubkey, *data.IPAddr) } ret[AddrFmtPubkeyIPPort] = fmt.Sprintf("%X@%s:%d", data.Pubkey, *data.IPAddr, data.Port) @@ -190,7 +190,7 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { } else { // If there's only 1 part then there's 3 mutually exclusive options. - if strings.HasPrefix(pkdata[0], PkhBech32Prefix + "1") { + if strings.HasPrefix(pkdata[0], LitBech32Prefix + "1") { addrdata.Pkh = &pkdata[0] } else if strings.HasPrefix(pkdata[0], PubkeyBech32Prefix + "1") { _, data, err := bech32.Decode(pkdata[0]) @@ -232,7 +232,7 @@ func ParseLnAddrData(addr LnAddr) (*LnAddressData, error) { } addrdata.Port = uint16(port) } else { - addrdata.Port = LnDefaultPort + addrdata.Port = DefaultPort } } @@ -271,7 +271,7 @@ func ParseLnAddr(m string) (LnAddr, error) { if pkherr != nil { return "", fmt.Errorf("invalid pkh bech32") } - if pkhprefix != PkhBech32Prefix { + if pkhprefix != LitBech32Prefix { return "", fmt.Errorf("pkh bech32 prefix incorrect") } @@ -299,7 +299,7 @@ func ParseLnAddr(m string) (LnAddr, error) { } } - if err == nil && prefix != PkhBech32Prefix && prefix != PubkeyBech32Prefix { + if err == nil && prefix != LitBech32Prefix && prefix != PubkeyBech32Prefix { return "", fmt.Errorf("pubkey (or phk) bech32 prefix incorrect") } } diff --git a/lncore/addr_test.go b/lncore/addr_test.go index 7cf2a8b0c..4e180e676 100644 --- a/lncore/addr_test.go +++ b/lncore/addr_test.go @@ -47,7 +47,7 @@ func makeRandomAddressData(seed int64) *LnAddressData { Pkh: nil, Pubkey: nil, IPAddr: nil, - Port: LnDefaultPort, + Port: DefaultPort, } mkpkhnopk := r.Int() % 4 == 0