Start refactoring protocol code into sub package. Move Packet Encoding and list of Commands. Change SetSelfReportedPorts parameters.

More related commits to follow!
This commit is contained in:
Kleissner
2021-10-17 16:28:19 +02:00
parent 2476146949
commit 0c7b1e0e03
18 changed files with 149 additions and 115 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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) {}
}
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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<<ActionSequenceLast) > 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) {

View File

@@ -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.

View File

@@ -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 {

27
protocol/Command.go Normal file
View File

@@ -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]
)

18
protocol/Hash.go Normal file
View File

@@ -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

View File

@@ -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)
}

3
protocol/readme.md Normal file
View File

@@ -0,0 +1,3 @@
# Protocol
The Peernet Protocol is defined in the Whitepaper and implemented here accordingly.