From 5799cdb3ea599b4e097cb3bd204b4cb19475a1f0 Mon Sep 17 00:00:00 2001 From: anthdm Date: Sun, 28 Jan 2018 16:18:48 +0100 Subject: [PATCH] handle address list message. --- pkg/network/message.go | 21 +++---- pkg/network/payload/addr.go | 99 ++++++++++++++++++++++++-------- pkg/network/payload/addr_test.go | 48 +++++++++++++++- pkg/network/server.go | 10 ++-- 4 files changed, 139 insertions(+), 39 deletions(-) diff --git a/pkg/network/message.go b/pkg/network/message.go index f1c27e709..596ef664f 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -165,6 +165,7 @@ func (m *Message) decodePayload(r io.Reader) error { } // Compare the checksum of the payload. + fmt.Println(len(pbuf)) if !compareChecksum(m.Checksum, pbuf) { return errors.New("checksum mismatch error") } @@ -177,16 +178,16 @@ func (m *Message) decodePayload(r io.Reader) error { if err := p.DecodeBinary(rr); err != nil { return err } - // case cmdInv: - // p = &payload.Inventory{} - // if err := p.UnmarshalBinary(pbuf); err != nil { - // return err - // } - // case cmdAddr: - // p = &payload.AddressList{} - // if err := p.UnmarshalBinary(pbuf); err != nil { - // return err - // } + // case cmdInv: + // p = &payload.Inventory{} + // if err := p.UnmarshalBinary(pbuf); err != nil { + // return err + // } + case cmdAddr: + p = &payload.AddressList{} + if err := p.DecodeBinary(rr); err != nil { + return err + } } m.Payload = p diff --git a/pkg/network/payload/addr.go b/pkg/network/payload/addr.go index 9711a0ece..880af540d 100644 --- a/pkg/network/payload/addr.go +++ b/pkg/network/payload/addr.go @@ -1,9 +1,11 @@ package payload import ( - "bytes" "encoding/binary" "fmt" + "io" + "strconv" + "strings" ) // Endpoint host + port of a node, compatible with net.Addr. @@ -12,6 +14,33 @@ type Endpoint struct { Port uint16 } +// EndpointFromString returns an Endpoint from the given string. +// For now this only handles the most simple hostport form. +// e.g. 127.0.0.1:3000 +// This should be enough to work with for now. +func EndpointFromString(s string) (Endpoint, error) { + hostPort := strings.Split(s, ":") + if len(hostPort) != 2 { + return Endpoint{}, fmt.Errorf("invalid address string: %s", s) + } + host := hostPort[0] + port := hostPort[1] + + ch := strings.Split(host, ".") + + buf := [16]byte{} + var n int + for i := 0; i < len(ch); i++ { + n = 12 + i + nn, _ := strconv.Atoi(ch[i]) + buf[n] = byte(nn) + } + + p, _ := strconv.Atoi(port) + + return Endpoint{buf, uint16(p)}, nil +} + // Network implements the net.Addr interface. func (e Endpoint) Network() string { return "tcp" } @@ -32,50 +61,72 @@ type AddrWithTime struct { Addr Endpoint } -// Size implements the payloader interface. +func NewAddrWithTime(addr Endpoint) *AddrWithTime { + return &AddrWithTime{ + Timestamp: 1337, + Services: 1, + Addr: addr, + } +} + +// Size implements the payload interface. func (p *AddrWithTime) Size() uint32 { return 30 } -// UnmarshalBinary implements the Payloader interface. -func (p *AddrWithTime) UnmarshalBinary(b []byte) error { - p.Timestamp = binary.LittleEndian.Uint32(b[0:4]) - p.Services = binary.LittleEndian.Uint64(b[4:12]) - binary.Read(bytes.NewReader(b[12:28]), binary.BigEndian, &p.Addr.IP) - p.Addr.Port = binary.BigEndian.Uint16(b[28:30]) - return nil +// DecodeBinary implements the Payload interface. +func (p *AddrWithTime) DecodeBinary(r io.Reader) error { + err := binary.Read(r, binary.LittleEndian, &p.Timestamp) + err = binary.Read(r, binary.LittleEndian, &p.Services) + err = binary.Read(r, binary.BigEndian, &p.Addr.IP) + err = binary.Read(r, binary.BigEndian, &p.Addr.Port) + + return err } -// MarshalBinary implements the Payloader interface. -func (p *AddrWithTime) MarshalBinary() ([]byte, error) { - return nil, nil +// EncodeBinary implements the Payload interface. +func (p *AddrWithTime) EncodeBinary(w io.Writer) error { + err := binary.Write(w, binary.LittleEndian, p.Timestamp) + err = binary.Write(w, binary.LittleEndian, p.Services) + err = binary.Write(w, binary.BigEndian, p.Addr.IP) + err = binary.Write(w, binary.BigEndian, p.Addr.Port) + + return err } -// AddressList contains a slice of AddrWithTime. +// AddressList holds a slice of AddrWithTime. type AddressList struct { Addrs []*AddrWithTime } -// UnmarshalBinary implements the Payloader interface. -func (p *AddressList) UnmarshalBinary(b []byte) error { +// DecodeBinary implements the Payload interface. +func (p *AddressList) DecodeBinary(r io.Reader) error { var lenList uint8 - binary.Read(bytes.NewReader(b[0:1]), binary.LittleEndian, &lenList) + binary.Read(r, binary.LittleEndian, &lenList) - offset := 1 // skip the uint8 length byte. - size := 30 // size of AddrWithTime - for i := 0; i < int(lenList); i++ { + for i := 0; i < int(4); i++ { address := &AddrWithTime{} - address.UnmarshalBinary(b[offset : offset+size]) + if err := address.DecodeBinary(r); err != nil { + return err + } p.Addrs = append(p.Addrs, address) - offset += size } return nil } -// MarshalBinary implements the Payloader interface. -func (p *AddressList) MarshalBinary() ([]byte, error) { - return nil, nil +// EncodeBinary implements the Payload interface. +func (p *AddressList) EncodeBinary(w io.Writer) error { + // Write the length of the slice + binary.Write(w, binary.LittleEndian, uint8(len(p.Addrs))) + + for _, addr := range p.Addrs { + if err := addr.EncodeBinary(w); err != nil { + return err + } + } + + return nil } // Size implements the Payloader interface. diff --git a/pkg/network/payload/addr_test.go b/pkg/network/payload/addr_test.go index 315c40303..35538aa33 100644 --- a/pkg/network/payload/addr_test.go +++ b/pkg/network/payload/addr_test.go @@ -1,8 +1,54 @@ package payload import ( + "bytes" + "fmt" + "reflect" "testing" ) -func TestNewAddrWithTime(t *testing.T) { +func TestEncodeDecodeAddr(t *testing.T) { + e, err := EndpointFromString("127.0.0.1:2000") + if err != nil { + t.Fatal(err) + } + + addr := NewAddrWithTime(e) + buf := new(bytes.Buffer) + if err := addr.EncodeBinary(buf); err != nil { + t.Fatal(err) + } + + addrDecode := &AddrWithTime{} + if err := addrDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(addr, addrDecode) { + t.Fatalf("expected both addr payloads to be equal: %v and %v", addr, addrDecode) + } +} + +func TestEncodeDecodeAddressList(t *testing.T) { + var lenList uint8 = 4 + addrs := make([]*AddrWithTime, lenList) + for i := 0; i < int(lenList); i++ { + e, _ := EndpointFromString(fmt.Sprintf("127.0.0.1:200%d", i)) + addrs[i] = NewAddrWithTime(e) + } + + buf := new(bytes.Buffer) + addrList := &AddressList{addrs} + if err := addrList.EncodeBinary(buf); err != nil { + t.Fatal(err) + } + + addrListDecode := &AddressList{} + if err := addrListDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(addrList, addrListDecode) { + t.Fatalf("expected both address list payloads to be equal: %v and %v", addrList, addrListDecode) + } } diff --git a/pkg/network/server.go b/pkg/network/server.go index 8795c8ea1..64a4fb716 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -183,7 +183,7 @@ func (s *Server) processMessage(msg *Message, peer *Peer) error { case cmdGetAddr: // return s.handleGetAddrCmd(msg, peer) case cmdAddr: - // return s.handleAddrCmd(msg.Payload.(*payload.AddressList), peer) + return s.handleAddrCmd(msg.Payload.(*payload.AddressList), peer) case cmdGetHeaders: case cmdHeaders: case cmdGetBlocks: @@ -231,9 +231,10 @@ func (s *Server) handleVersionCmd(v *payload.Version, peer *Peer) error { // When the remote node reveals its known peers we try to connect to all of them. func (s *Server) handleAddrCmd(addrList *payload.AddressList, peer *Peer) error { for _, addr := range addrList.Addrs { - if !s.addrAlreadyConnected(addr.Addr) { - go connectToRemoteNode(s, addr.Addr.String()) - } + fmt.Println(addr) + // if !s.addrAlreadyConnected(addr.Addr) { + // go connectToRemoteNode(s, addr.Addr.String()) + // } } return nil } @@ -269,6 +270,7 @@ func (s *Server) handleGetAddrCmd(msg *Message, peer *Peer) error { } func (s *Server) startProtocol(peer *Peer) { + // TODO: check if this peer is still connected. for { getaddrMsg := newMessage(s.net, cmdGetAddr, nil) peer.send <- getaddrMsg