package lndc import ( "bytes" "encoding/binary" "encoding/hex" "fmt" "net" "strings" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcutil" ) // lnAddr... // TODO(roasbeef): revamp type LNAdr struct { LnID [16]byte // redundant because adr contains it PubKey *btcec.PublicKey Base58Adr btcutil.Address // Base58 encoded address (1XXX...) NetAddr *net.TCPAddr // IP address name string // human readable name? Not a thing yet. host string // internet host this ID is reachable at. also not a thing endorsement []byte // a sig confirming the name? Not implemented net *chaincfg.Params } // newLnAdr.... func NewLnAdr(addr *net.TCPAddr, pubkey *btcec.PublicKey, net *chaincfg.Params) (*LNAdr, error) { hash160 := btcutil.Hash160(pubkey.SerializeCompressed()) pkh, err := btcutil.NewAddressPubKeyHash(hash160, net) if err != nil { return nil, err } return &LNAdr{ PubKey: pubkey, Base58Adr: pkh, NetAddr: addr, net: net, }, nil } // newLnAddr... func LnAddrFromString(encodedAddr string, netParams *chaincfg.Params) (*LNAdr, error) { // The format of an lnaddr is "@host" idHost := strings.Split(encodedAddr, "@") if len(idHost) != 2 { return nil, fmt.Errorf("invalid format for lnaddr string: %v", encodedAddr) } // Attempt to resolve the IP address, this handles parsing IPv6 zones, // and such. ipAddr, err := net.ResolveTCPAddr("tcp", idHost[1]) if err != nil { return nil, err } addr := &LNAdr{NetAddr: ipAddr, net: netParams} idLen := len(idHost[0]) switch { // Is the ID a hex-encoded compressed public key? case idLen > 65 && idLen < 69: pubkeyBytes, err := hex.DecodeString(idHost[0]) if err != nil { return nil, err } addr.PubKey, err = btcec.ParsePubKey(pubkeyBytes, btcec.S256()) if err != nil { return nil, err } // got pubey, populate address from pubkey pkh := btcutil.Hash160(addr.PubKey.SerializeCompressed()) addr.Base58Adr, err = btcutil.NewAddressPubKeyHash(pkh, netParams) if err != nil { return nil, err } // Is the ID a string encoded bitcoin address? case idLen > 33 && idLen < 37: addr.Base58Adr, err = btcutil.DecodeAddress(idHost[0], netParams) if err != nil { return nil, err } default: return nil, fmt.Errorf("invalid address %s", idHost[0]) } // Finally, populate the lnid from the address. copy(addr.LnID[:], addr.Base58Adr.ScriptAddress()) return addr, nil } // Deserialize an LNId from byte slice (on disk) // Note that this does not check any internal consistency, because on local // storage there's no point. Check separately if needed. // Also, old and probably needs to be changed / updated func (l *LNAdr) Deserialize(s []byte) error { b := bytes.NewBuffer(s) // Fail if on-disk LNId too short if b.Len() < 24 { // 24 is min lenght return fmt.Errorf("can't read LNId - too short") } // read indicator of pubkey or pubkeyhash x, err := b.ReadByte() if err != nil { return err } if x == 0xb0 { // for pubkey storage // read 33 bytes of pubkey l.PubKey, err = btcec.ParsePubKey(b.Next(33), btcec.S256()) if err != nil { return err } l.Base58Adr, err = btcutil.NewAddressPubKeyHash( btcutil.Hash160(l.PubKey.SerializeCompressed()), &chaincfg.TestNet3Params) if err != nil { return err } } else if x == 0xa0 { // for pubkeyhash storage l.Base58Adr, err = btcutil.NewAddressPubKeyHash( b.Next(20), &chaincfg.TestNet3Params) if err != nil { return err } } else { return fmt.Errorf("Unknown lnid indicator byte %x", x) } var nameLen, hostLen, endorseLen uint8 // read name length err = binary.Read(b, binary.BigEndian, &nameLen) if err != nil { return err } // if name non-zero, read name if nameLen > 0 { l.name = string(b.Next(int(nameLen))) } // read host length err = binary.Read(b, binary.BigEndian, &hostLen) if err != nil { return err } // if host non-zero, read host if hostLen > 0 { l.host = string(b.Next(int(hostLen))) } // read endorsement length err = binary.Read(b, binary.BigEndian, &endorseLen) if err != nil { return err } // if endorsement non-zero, read endorsement if endorseLen > 0 { l.endorsement = b.Next(int(endorseLen)) } return nil } // String... func (l *LNAdr) String() string { var encodedId []byte if l.Base58Adr != nil { encodedId = l.Base58Adr.ScriptAddress() } else { pubKey := l.PubKey.SerializeCompressed() pkh, _ := btcutil.NewAddressPubKeyHash(pubKey, l.net) encodedId = pkh.ScriptAddress() } return fmt.Sprintf("%v@%v", hex.EncodeToString(encodedId), l.NetAddr) }