diff --git a/Blockchain.go b/Blockchain.go index e03d165..a2ea0cc 100644 --- a/Blockchain.go +++ b/Blockchain.go @@ -10,6 +10,7 @@ import ( "os" "github.com/PeernetOfficial/core/blockchain" + "github.com/PeernetOfficial/core/protocol" ) // UserBlockchain is the user's blockchain and exports functions to directly read and write it @@ -21,7 +22,7 @@ const filenameUserBlockchain = "self.blockchain" // initUserBlockchain initializes the users blockchain. It creates the blockchain file if it does not exist already. // If it is corrupted, it will log the error and exit the process. func initUserBlockchain() { - blockchain.HashFunction = hashData + blockchain.HashFunction = protocol.HashData blockchain.PublicKey2NodeID = PublicKey2NodeID var err error diff --git a/Bootstrap.go b/Bootstrap.go index bd42979..db79321 100644 --- a/Bootstrap.go +++ b/Bootstrap.go @@ -20,6 +20,7 @@ import ( "sync" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -210,7 +211,7 @@ func contactArbitraryPeer(publicKey *btcec.PublicKey, address *net.UDPAddr, rece if len(packets) == 0 || packets[0].err != nil { return false } - raw := &PacketRaw{Command: CommandAnnouncement, Payload: packets[0].raw} + raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packets[0].raw} Filters.MessageOutAnnouncement(publicKey, nil, raw, findSelf, nil, nil, nil) diff --git a/Command Traverse.go b/Command Traverse.go index 727b638..dc31c9f 100644 --- a/Command Traverse.go +++ b/Command Traverse.go @@ -9,6 +9,8 @@ package core import ( "math/rand" "time" + + "github.com/PeernetOfficial/core/protocol" ) // cmdTraverseForward handles an incoming traverse message that should be forwarded to another peer @@ -45,7 +47,7 @@ func (peer *PeerInfo) cmdTraverseForward(msg *MessageTraverse) { return } - peerTarget.send(&PacketRaw{Command: CommandTraverse, Payload: msg.Payload}) + peerTarget.send(&protocol.PacketRaw{Command: protocol.CommandTraverse, Payload: msg.Payload}) } func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { @@ -82,7 +84,7 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { // ---- fork packetWorker to decode and validate embedded packet --- // Due to missing connection and other embedded details in the message (such as ports), the packet is not just simply queued to rawPacketsIncoming. - decoded, senderPublicKey, err := PacketDecrypt(msg.EmbeddedPacketRaw, peerPublicKey) + decoded, senderPublicKey, err := protocol.PacketDecrypt(msg.EmbeddedPacketRaw, peerPublicKey) if err != nil { return } @@ -92,7 +94,7 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { return } else if decoded.Protocol != 0 { return - } else if decoded.Command != CommandAnnouncement { + } else if decoded.Command != protocol.CommandAnnouncement { return } @@ -102,7 +104,7 @@ func (peer *PeerInfo) cmdTraverseReceive(msg *MessageTraverse) { // process it! switch decoded.Command { - case CommandAnnouncement: // Announce + case protocol.CommandAnnouncement: // Announce if announce, _ := msgDecodeAnnouncement(raw); announce != nil { if len(announce.UserAgent) > 0 { peerV.UserAgent = announce.UserAgent diff --git a/Commands.go b/Commands.go index 54a0566..061937d 100644 --- a/Commands.go +++ b/Commands.go @@ -11,6 +11,7 @@ import ( "fmt" "github.com/PeernetOfficial/core/dht" + "github.com/PeernetOfficial/core/protocol" ) // respondClosesContactsCount is the number of closest contact to respond. @@ -198,7 +199,7 @@ func (peer *PeerInfo) cmdPing(msg *MessageRaw) { return } - raw := &PacketRaw{Command: CommandPong, Sequence: msg.Sequence} + raw := &protocol.PacketRaw{Command: protocol.CommandPong, Sequence: msg.Sequence} Filters.MessageOutPong(peer, raw) diff --git a/Connection.go b/Connection.go index 1c451f2..f3bc3a4 100644 --- a/Connection.go +++ b/Connection.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -319,17 +320,17 @@ func (peer *PeerInfo) IsPortForward() (result bool) { // ---- sending code ---- // send sends the packet to the peer on the connection -func (c *Connection) send(packet *PacketRaw, receiverPublicKey *btcec.PublicKey, isFirstPacket bool) (err error) { +func (c *Connection) send(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, isFirstPacket bool) (err error) { if c == nil { return errors.New("invalid connection") } packet.Protocol = ProtocolVersion - packet.setSelfReportedPorts(c.Network) + packet.SetSelfReportedPorts(c.Network.SelfReportedPorts()) Filters.PacketOut(packet, receiverPublicKey, c) - raw, err := PacketEncrypt(peerPrivateKey, receiverPublicKey, packet) + raw, err := protocol.PacketEncrypt(peerPrivateKey, receiverPublicKey, packet) if err != nil { return err } @@ -339,7 +340,7 @@ func (c *Connection) send(packet *PacketRaw, receiverPublicKey *btcec.PublicKey, err = c.Network.send(c.Address.IP, c.Address.Port, raw) // Send Traverse message if the peer is behind a NAT and this is the first message. Only for Announcement. - if err == nil && isFirstPacket && c.IsBehindNAT() && c.traversePeer != nil && packet.Command == CommandAnnouncement { + if err == nil && isFirstPacket && c.IsBehindNAT() && c.traversePeer != nil && packet.Command == protocol.CommandAnnouncement { c.traversePeer.sendTraverse(packet, receiverPublicKey) } @@ -347,7 +348,7 @@ func (c *Connection) send(packet *PacketRaw, receiverPublicKey *btcec.PublicKey, } // send sends a raw packet to the peer. Only uses active connections. -func (peer *PeerInfo) send(packet *PacketRaw) (err error) { +func (peer *PeerInfo) send(packet *protocol.PacketRaw) (err error) { if peer.isVirtual { // special case for peers that were not contacted before for _, address := range peer.targetAddresses { sendAllNetworks(peer.PublicKey, packet, &net.UDPAddr{IP: address.IP, Port: int(address.Port)}, address.PortInternal, peer.traversePeer, nil) @@ -397,7 +398,7 @@ func (peer *PeerInfo) send(packet *PacketRaw) (err error) { } // sendConnection sends a packet to the peer using the specific connection -func (peer *PeerInfo) sendConnection(packet *PacketRaw, connection *Connection) (err error) { +func (peer *PeerInfo) sendConnection(packet *protocol.PacketRaw, connection *Connection) (err error) { isFirstPacketOut := atomic.LoadUint64(&peer.StatsPacketSent) == 0 && atomic.LoadUint64(&peer.StatsPacketReceived) == 0 atomic.AddUint64(&peer.StatsPacketSent, 1) @@ -406,7 +407,7 @@ func (peer *PeerInfo) sendConnection(packet *PacketRaw, connection *Connection) // sendAllNetworks sends a raw packet via all networks. It assigns a new sequence for each sent packet. // receiverPortInternal is important for NAT detection and sending the traverse message. -func sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *PacketRaw, remote *net.UDPAddr, receiverPortInternal uint16, traversePeer *PeerInfo, sequenceData interface{}) (err error) { +func sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *protocol.PacketRaw, remote *net.UDPAddr, receiverPortInternal uint16, traversePeer *PeerInfo, sequenceData interface{}) (err error) { networksMutex.RLock() defer networksMutex.RUnlock() diff --git a/Filter.go b/Filter.go index e9aec53..90b1659 100644 --- a/Filter.go +++ b/Filter.go @@ -12,6 +12,7 @@ import ( "log" "github.com/PeernetOfficial/core/dht" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -37,30 +38,30 @@ var Filters struct { // PacketIn is a low-level filter for incoming packets after they are decrypted. // Traverse messages are not covered. - PacketIn func(packet *PacketRaw, senderPublicKey *btcec.PublicKey, connection *Connection) + PacketIn func(packet *protocol.PacketRaw, senderPublicKey *btcec.PublicKey, connection *Connection) // PacketOut is a low-level filter for outgoing packets before they are encrypted. // IPv4 broadcast, IPv6 multicast, and Traverse messages are not covered. - PacketOut func(packet *PacketRaw, receiverPublicKey *btcec.PublicKey, connection *Connection) + PacketOut func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, connection *Connection) // MessageIn is a high-level filter for decoded incoming messages. message is of type nil, MessageAnnouncement, MessageResponse, or MessageTraverse MessageIn func(peer *PeerInfo, raw *MessageRaw, message interface{}) // MessageOutAnnouncement is a high-level filter for outgoing announcements. Peer is nil on first contact. // Broadcast and Multicast messages are not covered. - MessageOutAnnouncement func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) + MessageOutAnnouncement func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) // MessageOutResponse is a high-level filter for outgoing responses. - MessageOutResponse func(peer *PeerInfo, packet *PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) + MessageOutResponse func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) // MessageOutTraverse is a high-level filter for outgoing traverse messages. - MessageOutTraverse func(peer *PeerInfo, packet *PacketRaw, embeddedPacket *PacketRaw, receiverEnd *btcec.PublicKey) + MessageOutTraverse func(peer *PeerInfo, packet *protocol.PacketRaw, embeddedPacket *protocol.PacketRaw, receiverEnd *btcec.PublicKey) // MessageOutPing is a high-level filter for outgoing pings. - MessageOutPing func(peer *PeerInfo, packet *PacketRaw, connection *Connection) + MessageOutPing func(peer *PeerInfo, packet *protocol.PacketRaw, connection *Connection) // MessageOutPong is a high-level filter for outgoing pongs. - MessageOutPong func(peer *PeerInfo, packet *PacketRaw) + MessageOutPong func(peer *PeerInfo, packet *protocol.PacketRaw) } func initFilters() { @@ -83,30 +84,31 @@ func initFilters() { Filters.IncomingRequest = func(peer *PeerInfo, Action int, Key []byte, Info interface{}) {} } if Filters.PacketIn == nil { - Filters.PacketIn = func(packet *PacketRaw, senderPublicKey *btcec.PublicKey, c *Connection) {} + Filters.PacketIn = func(packet *protocol.PacketRaw, senderPublicKey *btcec.PublicKey, c *Connection) {} } if Filters.PacketOut == nil { - Filters.PacketOut = func(packet *PacketRaw, receiverPublicKey *btcec.PublicKey, c *Connection) {} + Filters.PacketOut = func(packet *protocol.PacketRaw, receiverPublicKey *btcec.PublicKey, c *Connection) {} } if Filters.MessageIn == nil { Filters.MessageIn = func(peer *PeerInfo, raw *MessageRaw, message interface{}) {} } if Filters.MessageOutAnnouncement == nil { - Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) { + Filters.MessageOutAnnouncement = func(receiverPublicKey *btcec.PublicKey, peer *PeerInfo, packet *protocol.PacketRaw, findSelf bool, findPeer []KeyHash, findValue []KeyHash, files []InfoStore) { } } if Filters.MessageOutResponse == nil { - Filters.MessageOutResponse = func(peer *PeerInfo, packet *PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) { + Filters.MessageOutResponse = func(peer *PeerInfo, packet *protocol.PacketRaw, hash2Peers []Hash2Peer, filesEmbed []EmbeddedFileData, hashesNotFound [][]byte) { } } if Filters.MessageOutTraverse == nil { - Filters.MessageOutTraverse = func(peer *PeerInfo, packet *PacketRaw, embeddedPacket *PacketRaw, receiverEnd *btcec.PublicKey) {} + Filters.MessageOutTraverse = func(peer *PeerInfo, packet *protocol.PacketRaw, embeddedPacket *protocol.PacketRaw, receiverEnd *btcec.PublicKey) { + } } if Filters.MessageOutPing == nil { - Filters.MessageOutPing = func(peer *PeerInfo, packet *PacketRaw, connection *Connection) {} + Filters.MessageOutPing = func(peer *PeerInfo, packet *protocol.PacketRaw, connection *Connection) {} } if Filters.MessageOutPong == nil { - Filters.MessageOutPong = func(peer *PeerInfo, packet *PacketRaw) {} + Filters.MessageOutPong = func(peer *PeerInfo, packet *protocol.PacketRaw) {} } } diff --git a/Kademlia.go b/Kademlia.go index a987ea2..e50638b 100644 --- a/Kademlia.go +++ b/Kademlia.go @@ -11,6 +11,7 @@ import ( "time" "github.com/PeernetOfficial/core/dht" + "github.com/PeernetOfficial/core/protocol" ) const alpha = 5 // Count of nodes to be contacted in parallel for finding a key @@ -127,7 +128,7 @@ func (peer *PeerInfo) sendAnnouncementStore(fileHash []byte, fileSize uint64) { // Data2Hash returns the hash for the data func Data2Hash(data []byte) (hash []byte) { - return hashData(data) + return protocol.HashData(data) } // GetData returns the requested data. It checks first the local store and then tries via DHT. @@ -152,14 +153,14 @@ func GetDataDHT(hash []byte) (data []byte, senderNodeID []byte, found bool) { // StoreDataLocal stores data into the local warehouse. func StoreDataLocal(data []byte) error { - key := hashData(data) + key := protocol.HashData(data) return Warehouse.Set(key, data) } // StoreDataDHT stores data locally and informs closestCount peers in the DHT about it. // Remote peers may choose to keep a record (in case another peers asks) or mirror the full data. func StoreDataDHT(data []byte, closestCount int) error { - key := hashData(data) + key := protocol.HashData(data) if err := Warehouse.Set(key, data); err != nil { return err } diff --git a/Message Encoding.go b/Message Encoding.go index b6aa6da..db727c8 100644 --- a/Message Encoding.go +++ b/Message Encoding.go @@ -16,6 +16,7 @@ import ( "time" "unicode/utf8" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -25,25 +26,6 @@ const ProtocolVersion = 0 // UserAgent should be set by the caller var UserAgent = "Peernet Core/0.1" -// Commands between peers -const ( - // Peer List Management - CommandAnnouncement = 0 // Announcement - CommandResponse = 1 // Response - CommandPing = 2 // Keep-alive message (no payload). - CommandPong = 3 // Response to ping (no payload). - CommandLocalDiscovery = 4 // Local discovery - CommandTraverse = 5 // Help establish a connection between 2 remote peers - - // Blockchain - CommandGet = 6 // Request blocks for specified peer. - - // File Discovery - - // Debug - CommandChat = 10 // Chat message [debug] -) - // Actions between peers, sent via Announcement message. They correspond to the bit array index. const ( ActionFindSelf = 0 // FIND_SELF Request closest neighbors to self @@ -65,7 +47,7 @@ const ( // MessageRaw is a high-level message between peers that has not been decoded type MessageRaw struct { - PacketRaw + protocol.PacketRaw SenderPublicKey *btcec.PublicKey // Sender Public Key, ECDSA (secp256k1) 257-bit connection *Connection // Connection that received the packet sequence *sequenceExpiry // Sequence @@ -478,7 +460,7 @@ func decodeEmbeddedFile(data []byte, count int) (filesEmbed []EmbeddedFileData, index += sizeField // validate the hash - if !bytes.Equal(hash, hashData(fileData)) { + if !bytes.Equal(hash, protocol.HashData(fileData)) { return nil, read, false } @@ -494,7 +476,7 @@ const udpMaxPacketSize = 65507 // isPacketSizeExceed checks if the max packet size would be exceeded with the payload func isPacketSizeExceed(currentSize int, testSize int) bool { - return currentSize+testSize > udpMaxPacketSize-packetLengthMin + return currentSize+testSize > udpMaxPacketSize-protocol.PacketLengthMin } func FeatureSupport() (feature byte) { @@ -656,7 +638,7 @@ createPacketLoop: } // EmbeddedFileSizeMax is the maximum size of embedded files in response messages. Any file exceeding that must be shared via regular file transfer. -const EmbeddedFileSizeMax = udpMaxPacketSize - packetLengthMin - announcementPayloadHeaderSize - 2 - 35 +const EmbeddedFileSizeMax = udpMaxPacketSize - protocol.PacketLengthMin - announcementPayloadHeaderSize - 2 - 35 // msgEncodeResponse encodes a response message // hash2Peers will be modified. @@ -826,19 +808,6 @@ func encodePeerRecord(raw []byte, peer *PeerRecord, reason uint8) { binary.LittleEndian.PutUint16(raw[63:63+2], peer.IPv6PortReportedExternal) } -// setSelfReportedPorts sets the fields Internal Port and External Port according to the connection details. -// This is important for the remote peer to make smart decisions whether this peer is behind a NAT/firewall and supports port forwarding/UPnP. -func (packet *PacketRaw) setSelfReportedPorts(n *Network) { - if packet.Command != CommandAnnouncement && packet.Command != CommandResponse { // only for Announcement and Response messages - return - } - - portI, portE := n.SelfReportedPorts() - - binary.LittleEndian.PutUint16(packet.Payload[15:17], portI) - binary.LittleEndian.PutUint16(packet.Payload[17:19], portE) -} - // ---- Traverse ---- const traversePayloadHeaderSize = 76 + 65 + 28 @@ -882,7 +851,7 @@ func msgDecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) { signature := msg.Payload[76+sizePacketEmbed : 76+sizePacketEmbed+65] - result.SignerPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature, hashData(msg.Payload[:76+sizePacketEmbed])) + result.SignerPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature, protocol.HashData(msg.Payload[:76+sizePacketEmbed])) if err != nil { return nil, err } @@ -932,7 +901,7 @@ func msgEncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []b copy(raw[76:76+sizePacketEmbed], embeddedPacketRaw) // add signature - signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, hashData(raw[:76+sizePacketEmbed]), true) + signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, protocol.HashData(raw[:76+sizePacketEmbed]), true) if err != nil { return nil, err } @@ -978,7 +947,7 @@ func msgEncodeTraverseSetAddress(raw []byte, connectionIPv4, connectionIPv6 *Con // pingConnection sends a ping to the target peer via the specified connection func (peer *PeerInfo) pingConnection(connection *Connection) { - raw := &PacketRaw{Command: CommandPing, Sequence: peer.msgNewSequence(nil).sequence} + raw := &protocol.PacketRaw{Command: protocol.CommandPing, Sequence: peer.msgNewSequence(nil).sequence} Filters.MessageOutPing(peer, raw, connection) err := peer.sendConnection(raw, connection) @@ -991,7 +960,7 @@ func (peer *PeerInfo) pingConnection(connection *Connection) { // Chat sends a text message func (peer *PeerInfo) Chat(text string) { - peer.send(&PacketRaw{Command: CommandChat, Payload: []byte(text)}) + peer.send(&protocol.PacketRaw{Command: protocol.CommandChat, Payload: []byte(text)}) } // sendAnnouncement sends the announcement message. It acquires a new sequence for each message. @@ -1000,7 +969,7 @@ func (peer *PeerInfo) sendAnnouncement(sendUA, findSelf bool, findPeer []KeyHash for _, packet := range packets { packet.sequence = peer.msgNewSequence(sequenceData) - raw := &PacketRaw{Command: CommandAnnouncement, Payload: packet.raw, Sequence: packet.sequence.sequence} + raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packet.raw, Sequence: packet.sequence.sequence} Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, findSelf, findPeer, findValue, files) packet.err = peer.send(raw) } @@ -1013,7 +982,7 @@ func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Ha packets, err := msgEncodeResponse(sendUA, hash2Peers, filesEmbed, hashesNotFound) for _, packet := range packets { - raw := &PacketRaw{Command: CommandResponse, Payload: packet, Sequence: sequence} + raw := &protocol.PacketRaw{Command: protocol.CommandResponse, Payload: packet, Sequence: sequence} Filters.MessageOutResponse(peer, raw, hash2Peers, filesEmbed, hashesNotFound) peer.send(raw) } @@ -1022,12 +991,12 @@ func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Ha } // sendTraverse sends a traverse message -func (peer *PeerInfo) sendTraverse(packet *PacketRaw, receiverEnd *btcec.PublicKey) (err error) { +func (peer *PeerInfo) sendTraverse(packet *protocol.PacketRaw, receiverEnd *btcec.PublicKey) (err error) { packet.Protocol = ProtocolVersion // self-reported ports are not set, as this isn't sent via a specific network but a relay - //packet.setSelfReportedPorts(c.Network) + //packet.SetSelfReportedPorts(c.Network.SelfReportedPorts()) - embeddedPacketRaw, err := PacketEncrypt(peerPrivateKey, receiverEnd, packet) + embeddedPacketRaw, err := protocol.PacketEncrypt(peerPrivateKey, receiverEnd, packet) if err != nil { return err } @@ -1037,7 +1006,7 @@ func (peer *PeerInfo) sendTraverse(packet *PacketRaw, receiverEnd *btcec.PublicK return err } - raw := &PacketRaw{Command: CommandTraverse, Payload: packetRaw} + raw := &protocol.PacketRaw{Command: protocol.CommandTraverse, Payload: packetRaw} Filters.MessageOutTraverse(peer, raw, packet, receiverEnd) diff --git a/Message Sequence.go b/Message Sequence.go index d48c7f1..c33532e 100644 --- a/Message Sequence.go +++ b/Message Sequence.go @@ -21,6 +21,7 @@ import ( "sync/atomic" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -97,7 +98,7 @@ func msgArbitrarySequence(publicKey *btcec.PublicKey, data interface{}) (info *s // msgValidateSequence validates the sequence number of an incoming message. It will set raw.sequence if valid. func msgValidateSequence(raw *MessageRaw, invalidate bool) (valid bool, rtt time.Duration) { // Only Response and Pong - if raw.Command != CommandResponse && raw.Command != CommandPong { + if raw.Command != protocol.CommandResponse && raw.Command != protocol.CommandPong { return true, rtt } @@ -123,7 +124,7 @@ func msgValidateSequence(raw *MessageRaw, invalidate bool) (valid bool, rtt time // invalidate the sequence immediately? if invalidate { delete(sequences, key) - } else if raw.Command == CommandResponse { + } else if raw.Command == protocol.CommandResponse { // Special case CommandResponse: Extend validity in case there are follow-up responses by half of the round-trip time since they will be sent one-way. sequence.expires = time.Now().Add(time.Duration(ReplyTimeout) * time.Second / 2) } @@ -136,7 +137,7 @@ func msgValidateSequence(raw *MessageRaw, invalidate bool) (valid bool, rtt time // msgInvalidateSequence invalidates the sequence number. func msgInvalidateSequence(raw *MessageRaw) { // Only Response - if raw.Command != CommandResponse { + if raw.Command != protocol.CommandResponse { return } diff --git a/Network IPv4 Broadcast.go b/Network IPv4 Broadcast.go index 1f3c773..4f5787f 100644 --- a/Network IPv4 Broadcast.go +++ b/Network IPv4 Broadcast.go @@ -14,6 +14,7 @@ import ( "strconv" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/PeernetOfficial/core/reuseport" "github.com/btcsuite/btcd/btcec" ) @@ -77,7 +78,7 @@ func (network *Network) BroadcastIPv4Listen() { //fmt.Printf("BroadcastIPv4Listen from %s at network %s\n", sender.String(), network.address.String()) - if length < packetLengthMin { + if length < protocol.PacketLengthMin { // Discard packets that do not meet the minimum length. continue } @@ -94,7 +95,7 @@ func (network *Network) BroadcastIPv4Send() (err error) { return packets[0].err } - raw, err := PacketEncrypt(peerPrivateKey, ipv4BroadcastPublicKey, &PacketRaw{Protocol: ProtocolVersion, Command: CommandLocalDiscovery, Payload: packets[0].raw}) + raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0].raw}) if err != nil { return err } diff --git a/Network IPv6 Multicast.go b/Network IPv6 Multicast.go index 75d8b93..beba9d3 100644 --- a/Network IPv6 Multicast.go +++ b/Network IPv6 Multicast.go @@ -22,6 +22,7 @@ import ( "strconv" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/PeernetOfficial/core/reuseport" "github.com/btcsuite/btcd/btcec" "golang.org/x/net/ipv6" @@ -123,7 +124,7 @@ func (network *Network) MulticastIPv6Listen() { //fmt.Printf("MulticastIPv6Listen from %s at network %s\n", sender.String(), network.address.String()) - if length < packetLengthMin { + if length < protocol.PacketLengthMin { // Discard packets that do not meet the minimum length. continue } @@ -140,7 +141,7 @@ func (network *Network) MulticastIPv6Send() (err error) { return packets[0].err } - raw, err := PacketEncrypt(peerPrivateKey, ipv6MulticastPublicKey, &PacketRaw{Protocol: ProtocolVersion, Command: CommandLocalDiscovery, Payload: packets[0].raw}) + raw, err := protocol.PacketEncrypt(peerPrivateKey, ipv6MulticastPublicKey, &protocol.PacketRaw{Protocol: ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0].raw}) if err != nil { return err } diff --git a/Network.go b/Network.go index 54347d9..4056ce9 100644 --- a/Network.go +++ b/Network.go @@ -13,6 +13,7 @@ import ( "sync/atomic" "time" + "github.com/PeernetOfficial/core/protocol" "github.com/PeernetOfficial/core/upnp" ) @@ -131,7 +132,7 @@ func (network *Network) Listen() { continue } - if length < packetLengthMin { + if length < protocol.PacketLengthMin { // Discard packets that do not meet the minimum length. continue } @@ -144,7 +145,7 @@ func (network *Network) Listen() { // packetWorker handles incoming packets. func packetWorker(packets <-chan networkWire) { for packet := range packets { - decoded, senderPublicKey, err := PacketDecrypt(packet.raw, packet.receiverPublicKey) + decoded, senderPublicKey, err := protocol.PacketDecrypt(packet.raw, packet.receiverPublicKey) if err != nil { //Filters.LogError("packetWorker", "decrypting packet from '%s': %s\n", packet.sender.String(), err.Error()) // Only log for debug purposes. continue @@ -177,7 +178,7 @@ func packetWorker(packets <-chan networkWire) { raw := &MessageRaw{SenderPublicKey: senderPublicKey, PacketRaw: *decoded, connection: connection} switch decoded.Command { - case CommandAnnouncement: // Announce + case protocol.CommandAnnouncement: // Announce if announce, _ := msgDecodeAnnouncement(raw); announce != nil { // Update known internal/external port and User Agent connection.PortInternal = announce.PortInternal @@ -194,7 +195,7 @@ func packetWorker(packets <-chan networkWire) { peer.cmdAnouncement(announce) } - case CommandResponse: // Response + case protocol.CommandResponse: // Response if response, _ := msgDecodeResponse(raw); response != nil { // Validate sequence number which prevents unsolicited responses. if valid, rtt := msgValidateSequence(raw, response.Actions&(1< 0); !valid { @@ -219,7 +220,7 @@ func packetWorker(packets <-chan networkWire) { peer.cmdResponse(response) } - case CommandLocalDiscovery: // Local discovery, sent via IPv4 broadcast and IPv6 multicast + case protocol.CommandLocalDiscovery: // Local discovery, sent via IPv4 broadcast and IPv6 multicast if announce, _ := msgDecodeAnnouncement(raw); announce != nil { if len(announce.UserAgent) > 0 { peer.UserAgent = announce.UserAgent @@ -233,11 +234,11 @@ func packetWorker(packets <-chan networkWire) { peer.cmdLocalDiscovery(announce) } - case CommandPing: // Ping + case protocol.CommandPing: // Ping Filters.MessageIn(peer, raw, nil) peer.cmdPing(raw) - case CommandPong: // Ping + case protocol.CommandPong: // Ping // Validate sequence number which prevents unsolicited responses. if valid, rtt := msgValidateSequence(raw, true); !valid { //Filters.LogError("packetWorker", "message with invalid sequence %d command %d from %s\n", raw.Sequence, raw.Command, raw.connection.Address.String()) // Only log for debug purposes. @@ -250,11 +251,11 @@ func packetWorker(packets <-chan networkWire) { peer.cmdPong(raw) - case CommandChat: // Chat [debug] + case protocol.CommandChat: // Chat [debug] Filters.MessageIn(peer, raw, nil) peer.cmdChat(raw) - case CommandTraverse: + case protocol.CommandTraverse: if traverse, _ := msgDecodeTraverse(raw); traverse != nil { Filters.MessageIn(peer, raw, traverse) if traverse.TargetPeer.IsEqual(peerPublicKey) && traverse.AuthorizedRelayPeer.IsEqual(peer.PublicKey) { diff --git a/Peer ID.go b/Peer ID.go index 6a0eabb..7333fb7 100644 --- a/Peer ID.go +++ b/Peer ID.go @@ -14,6 +14,7 @@ import ( "sync" "github.com/PeernetOfficial/core/dht" + "github.com/PeernetOfficial/core/protocol" "github.com/btcsuite/btcd/btcec" ) @@ -198,7 +199,7 @@ func publicKey2Compressed(publicKey *btcec.PublicKey) [btcec.PubKeyBytesLenCompr // PublicKey2NodeID translates the Public Key into the node ID used in the Kademlia network. func PublicKey2NodeID(publicKey *btcec.PublicKey) (nodeID []byte) { - return hashData(publicKey.SerializeCompressed()) + return protocol.HashData(publicKey.SerializeCompressed()) } // records2Nodes translates infoPeer structures to nodes. If the reported nodes are not in the peer table, it will create temporary PeerInfo structures. diff --git a/Test_test.go b/Test_test.go index 37730ef..63c36f0 100644 --- a/Test_test.go +++ b/Test_test.go @@ -4,6 +4,8 @@ package core import ( "fmt" "testing" + + "github.com/PeernetOfficial/core/protocol" ) func TestMessageEncodingAnnouncement(t *testing.T) { @@ -14,14 +16,14 @@ func TestMessageEncodingAnnouncement(t *testing.T) { } // encode and decode announcement - packetR := PacketRaw{Protocol: 0, Command: CommandAnnouncement, Sequence: 123} + packetR := protocol.PacketRaw{Protocol: 0, Command: protocol.CommandAnnouncement, Sequence: 123} var findPeer []KeyHash var findValue []KeyHash var files []InfoStore - hash1 := hashData([]byte("test")) - hash2 := hashData([]byte("test3")) + hash1 := protocol.HashData([]byte("test")) + hash2 := protocol.HashData([]byte("test3")) findPeer = append(findPeer, KeyHash{Hash: hash1}) findValue = append(findValue, KeyHash{Hash: hash2}) @@ -47,7 +49,7 @@ func TestMessageEncodingResponse(t *testing.T) { } // encode and decode response - packetR := PacketRaw{Protocol: 0, Command: CommandResponse} + packetR := protocol.PacketRaw{Protocol: 0, Command: protocol.CommandResponse} var hash2Peers []Hash2Peer var filesEmbed []EmbeddedFileData @@ -55,12 +57,12 @@ func TestMessageEncodingResponse(t *testing.T) { file1Data := []byte("test") file2Data := []byte("test3") - file1 := EmbeddedFileData{ID: KeyHash{hashData(file1Data)}, Data: file1Data} - file2 := EmbeddedFileData{ID: KeyHash{hashData(file2Data)}, Data: file2Data} + file1 := EmbeddedFileData{ID: KeyHash{protocol.HashData(file1Data)}, Data: file1Data} + file2 := EmbeddedFileData{ID: KeyHash{protocol.HashData(file2Data)}, Data: file2Data} filesEmbed = append(filesEmbed, file1) filesEmbed = append(filesEmbed, file2) - hashesNotFound = append(hashesNotFound, hashData([]byte("NA"))) + hashesNotFound = append(hashesNotFound, protocol.HashData([]byte("NA"))) packetsRaw, err := msgEncodeResponse(true, hash2Peers, filesEmbed, hashesNotFound) if err != nil { diff --git a/protocol/Command.go b/protocol/Command.go new file mode 100644 index 0000000..91b3234 --- /dev/null +++ b/protocol/Command.go @@ -0,0 +1,27 @@ +/* +File Name: Command.go +Copyright: 2021 Peernet s.r.o. +Author: Peter Kleissner +*/ + +package protocol + +// Commands between peers +const ( + // Peer List Management + CommandAnnouncement = 0 // Announcement + CommandResponse = 1 // Response + CommandPing = 2 // Keep-alive message (no payload). + CommandPong = 3 // Response to ping (no payload). + CommandLocalDiscovery = 4 // Local discovery + CommandTraverse = 5 // Help establish a connection between 2 remote peers + + // Blockchain + CommandGetBlock = 6 // Request blocks for specified peer. + + // File Discovery + CommandTransfer = 8 // File transfer. + + // Debug + CommandChat = 10 // Chat message [debug] +) diff --git a/protocol/Hash.go b/protocol/Hash.go new file mode 100644 index 0000000..83eb9dc --- /dev/null +++ b/protocol/Hash.go @@ -0,0 +1,18 @@ +/* +File Name: Hash.go +Copyright: 2021 Peernet s.r.o. +Author: Peter Kleissner +*/ + +package protocol + +import "lukechampine.com/blake3" + +// HashData abstracts the hash function. +func HashData(data []byte) (hash []byte) { + hash32 := blake3.Sum256(data) + return hash32[:] +} + +// HashSize is blake3 hash digest size = 256 bits +const HashSize = 32 diff --git a/Packet Encoding.go b/protocol/Packet Encoding.go similarity index 83% rename from Packet Encoding.go rename to protocol/Packet Encoding.go index 9e3b3bb..3044cfc 100644 --- a/Packet Encoding.go +++ b/protocol/Packet Encoding.go @@ -19,7 +19,7 @@ The signature is applied on the entire packet, which guarantees that the signatu Because the signature could be a possible fingerpint, it is encrypted itself. */ -package core +package protocol import ( "encoding/binary" @@ -28,7 +28,6 @@ import ( "github.com/btcsuite/btcd/btcec" "golang.org/x/crypto/salsa20" - "lukechampine.com/blake3" ) // PacketRaw is a decrypted P2P message @@ -40,7 +39,7 @@ type PacketRaw struct { } // The minimum packet size is 12 bytes (minimum header size) + 65 bytes (signature) -const packetLengthMin = 12 + signatureSize +const PacketLengthMin = 12 + signatureSize const signatureSize = 65 const maxRandomGarbage = 20 @@ -60,7 +59,7 @@ func PacketDecrypt(raw []byte, receiverPublicKey *btcec.PublicKey) (packet *Pack keySalsa := publicKeyToSalsa20Key(receiverPublicKey) salsa20.XORKeyStream(signature[:], signature[:], nonce, keySalsa) - senderPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature[:], hashData(raw[:len(raw)-signatureSize])) + senderPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature[:], HashData(raw[:len(raw)-signatureSize])) if err != nil { return nil, nil, err } @@ -87,8 +86,8 @@ func PacketDecrypt(raw []byte, receiverPublicKey *btcec.PublicKey) (packet *Pack // PacketEncrypt encrypts a packet using the provided senders private key and receivers compressed public key. func PacketEncrypt(senderPrivateKey *btcec.PrivateKey, receiverPublicKey *btcec.PublicKey, packet *PacketRaw) (raw []byte, err error) { - garbage := packetGarbage(packetLengthMin + len(packet.Payload)) - raw = make([]byte, packetLengthMin+len(packet.Payload)+len(garbage)) + garbage := packetGarbage(PacketLengthMin + len(packet.Payload)) + raw = make([]byte, PacketLengthMin+len(packet.Payload)+len(garbage)) nonceC := rand.Uint32() nonce := make([]byte, 8) @@ -109,7 +108,7 @@ func PacketEncrypt(senderPrivateKey *btcec.PrivateKey, receiverPublicKey *btcec. salsa20.XORKeyStream(raw[4:12+len(packet.Payload)+len(garbage)], raw[4:12+len(packet.Payload)+len(garbage)], nonce, keySalsa) // add signature - signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, hashData(raw[:len(raw)-signatureSize]), true) + signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, HashData(raw[:len(raw)-signatureSize]), true) if err != nil { return nil, err } @@ -146,11 +145,13 @@ func publicKeyToSalsa20Key(publicKey *btcec.PublicKey) (key *[32]byte) { return key } -// hashData abstracts the hash function. -func hashData(data []byte) (hash []byte) { - hash32 := blake3.Sum256(data) - return hash32[:] -} +// SetSelfReportedPorts sets the fields Internal Port and External Port according to the connection details. +// This is important for the remote peer to make smart decisions whether this peer is behind a NAT/firewall and supports port forwarding/UPnP. +func (packet *PacketRaw) SetSelfReportedPorts(portI, portE uint16) { + if packet.Command != CommandAnnouncement && packet.Command != CommandResponse { // only for Announcement and Response messages + return + } -// HashSize is blake3 hash digest size = 256 bits -const HashSize = 32 + binary.LittleEndian.PutUint16(packet.Payload[15:17], portI) + binary.LittleEndian.PutUint16(packet.Payload[17:19], portE) +} diff --git a/protocol/readme.md b/protocol/readme.md new file mode 100644 index 0000000..cf3707d --- /dev/null +++ b/protocol/readme.md @@ -0,0 +1,3 @@ +# Protocol + +The Peernet Protocol is defined in the Whitepaper and implemented here accordingly.