|
1 | | -# p2p_poc |
2 | | -# p2p_poc |
| 1 | +# P2P Messaging Library |
| 2 | + |
| 3 | +A simple, channel-based peer-to-peer messaging library built on libp2p. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Simple API**: Create a client, subscribe to topics, and publish messages with minimal code |
| 8 | +- **Channel-based**: Receive messages through Go channels for idiomatic concurrent programming |
| 9 | +- **Auto-discovery**: Automatic peer discovery via DHT, mDNS, and peer caching |
| 10 | +- **NAT traversal**: Built-in support for hole punching and relay connections |
| 11 | +- **Persistent peers**: Automatically caches and reconnects to known peers |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +```bash |
| 16 | +go get github.com/ordishs/p2p_poc |
| 17 | +``` |
| 18 | + |
| 19 | +## Quick Start |
| 20 | + |
| 21 | +```go |
| 22 | +package main |
| 23 | + |
| 24 | +import ( |
| 25 | + "fmt" |
| 26 | + "log" |
| 27 | + |
| 28 | + "github.com/ordishs/p2p_poc" |
| 29 | +) |
| 30 | + |
| 31 | +func main() { |
| 32 | + // Generate a private key (do this once and save it) |
| 33 | + keyHex, err := p2p.GeneratePrivateKeyHex() |
| 34 | + if err != nil { |
| 35 | + log.Fatal(err) |
| 36 | + } |
| 37 | + // In production, save keyHex to config file, env var, or database |
| 38 | + |
| 39 | + // Create a P2P client |
| 40 | + client, err := p2p.NewPeer(p2p.Config{ |
| 41 | + Name: "my-node", |
| 42 | + PrivateKeyHex: keyHex, |
| 43 | + }) |
| 44 | + if err != nil { |
| 45 | + log.Fatal(err) |
| 46 | + } |
| 47 | + defer client.Close() |
| 48 | + |
| 49 | + // Subscribe to a topic |
| 50 | + msgChan := client.Subscribe("my-topic") |
| 51 | + |
| 52 | + // Receive messages |
| 53 | + go func() { |
| 54 | + for msg := range msgChan { |
| 55 | + fmt.Printf("Received from %s: %s\n", msg.From, string(msg.Data)) |
| 56 | + } |
| 57 | + }() |
| 58 | + |
| 59 | + // Publish a message |
| 60 | + if err := client.Publish("my-topic", []byte("Hello, P2P!")); err != nil { |
| 61 | + log.Printf("Error publishing: %v", err) |
| 62 | + } |
| 63 | + |
| 64 | + // Get connected peers |
| 65 | + peers := client.GetPeers() |
| 66 | + for _, peer := range peers { |
| 67 | + fmt.Printf("Peer: %s [%s]\n", peer.Name, peer.ID) |
| 68 | + } |
| 69 | + |
| 70 | + select {} // Wait forever |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +## API Reference |
| 75 | + |
| 76 | +### Config |
| 77 | + |
| 78 | +```go |
| 79 | +type Config struct { |
| 80 | + Name string // Required: identifier for this peer |
| 81 | + BootstrapPeers []string // Optional: initial peers to connect to |
| 82 | + Logger Logger // Optional: custom logger (uses DefaultLogger if not provided) |
| 83 | + PrivateKey crypto.PrivKey // Required: private key for persistent peer ID |
| 84 | + PeerCacheFile string // Optional: file path for peer persistence |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +**Logger Interface:** |
| 89 | + |
| 90 | +The library defines a `Logger` interface and provides a `DefaultLogger` implementation: |
| 91 | + |
| 92 | +```go |
| 93 | +type Logger interface { |
| 94 | + Debugf(format string, v ...any) |
| 95 | + Infof(format string, v ...any) |
| 96 | + Warnf(format string, v ...any) |
| 97 | + Errorf(format string, v ...any) |
| 98 | +} |
| 99 | + |
| 100 | +// DefaultLogger is provided out of the box |
| 101 | +logger := &p2p.DefaultLogger{} |
| 102 | + |
| 103 | +// Or use your own custom logger that implements the interface |
| 104 | +``` |
| 105 | + |
| 106 | +**Persistent Peer Identity:** |
| 107 | + |
| 108 | +The `PrivateKeyHex` field is **required** to ensure consistent peer IDs across restarts: |
| 109 | + |
| 110 | +```go |
| 111 | +// Generate a new key for first-time setup |
| 112 | +keyHex, err := p2p.GeneratePrivateKeyHex() |
| 113 | +if err != nil { |
| 114 | + log.Fatal(err) |
| 115 | +} |
| 116 | +// Save keyHex somewhere (env var, config file, database, etc.) |
| 117 | + |
| 118 | +// Create client with the saved key |
| 119 | +client, err := p2p.NewPeer(p2p.Config{ |
| 120 | + Name: "node1", |
| 121 | + PrivateKeyHex: keyHex, |
| 122 | +}) |
| 123 | + |
| 124 | +// You can also retrieve the key from an existing client |
| 125 | +retrievedKey, _ := client.GetPrivateKeyHex() |
| 126 | +``` |
| 127 | + |
| 128 | +**Peer Persistence:** |
| 129 | + |
| 130 | +The `PeerCacheFile` field is optional and enables peer persistence for faster reconnection: |
| 131 | + |
| 132 | +```go |
| 133 | +client, err := p2p.NewPeer(p2p.Config{ |
| 134 | + Name: "node1", |
| 135 | + PrivateKey: privKey, |
| 136 | + PeerCacheFile: "peers.json", // Enable peer caching |
| 137 | +}) |
| 138 | +``` |
| 139 | + |
| 140 | +When enabled: |
| 141 | +- Connected peers are automatically saved to the specified file |
| 142 | +- On restart, the client will reconnect to previously known peers |
| 143 | +- This significantly speeds up network reconnection |
| 144 | +- If not provided, peer caching is disabled |
| 145 | + |
| 146 | +### Client |
| 147 | + |
| 148 | +#### GeneratePrivateKeyHex |
| 149 | + |
| 150 | +```go |
| 151 | +func GeneratePrivateKeyHex() (string, error) |
| 152 | +``` |
| 153 | + |
| 154 | +Generates a new Ed25519 private key and returns it as a hex string. Use this function to create a new key for `Config.PrivateKeyHex` when setting up a new peer for the first time. |
| 155 | + |
| 156 | +#### NewPeer |
| 157 | + |
| 158 | +```go |
| 159 | +func NewPeer(config Config) (*Client, error) |
| 160 | +``` |
| 161 | + |
| 162 | +Creates and starts a new P2P client. The client automatically: |
| 163 | +- Creates a libp2p host with NAT traversal support |
| 164 | +- Bootstraps to the DHT network |
| 165 | +- Starts peer discovery (DHT + mDNS) |
| 166 | +- Connects to cached peers from previous sessions |
| 167 | + |
| 168 | +**Note:** Requires `Config.PrivateKeyHex` to be set. Use `GeneratePrivateKeyHex()` to create a new key. |
| 169 | + |
| 170 | +#### Subscribe |
| 171 | + |
| 172 | +```go |
| 173 | +func (c *Client) Subscribe(topic string) <-chan Message |
| 174 | +``` |
| 175 | + |
| 176 | +Subscribes to a topic and returns a channel that receives messages. The channel is closed when the client is closed. |
| 177 | + |
| 178 | +#### Publish |
| 179 | + |
| 180 | +```go |
| 181 | +func (c *Client) Publish(topic string, data []byte) error |
| 182 | +``` |
| 183 | + |
| 184 | +Publishes a message to a topic. The message is broadcast to all peers subscribed to the topic. |
| 185 | + |
| 186 | +#### GetPeers |
| 187 | + |
| 188 | +```go |
| 189 | +func (c *Client) GetPeers() []PeerInfo |
| 190 | +``` |
| 191 | + |
| 192 | +Returns information about all known peers on subscribed topics. |
| 193 | + |
| 194 | +#### GetPrivateKeyHex |
| 195 | + |
| 196 | +```go |
| 197 | +func (c *Client) GetPrivateKeyHex() (string, error) |
| 198 | +``` |
| 199 | + |
| 200 | +Returns the hex-encoded private key for this peer. This can be saved and used in `Config.PrivateKey` to maintain the same peer ID across restarts. |
| 201 | + |
| 202 | +#### Close |
| 203 | + |
| 204 | +```go |
| 205 | +func (c *Client) Close() error |
| 206 | +``` |
| 207 | + |
| 208 | +Shuts down the client and releases all resources. |
| 209 | + |
| 210 | +### Message |
| 211 | + |
| 212 | +```go |
| 213 | +type Message struct { |
| 214 | + Topic string // Topic this message was received on |
| 215 | + From string // Sender's name |
| 216 | + FromID string // Sender's peer ID |
| 217 | + Data []byte // Message payload |
| 218 | + Timestamp time.Time // When the message was received |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +### PeerInfo |
| 223 | + |
| 224 | +```go |
| 225 | +type PeerInfo struct { |
| 226 | + ID string // Peer ID |
| 227 | + Name string // Peer name (if known) |
| 228 | + Addrs []string // Peer addresses |
| 229 | +} |
| 230 | +``` |
| 231 | + |
| 232 | +## Example |
| 233 | + |
| 234 | +See [example/main.go](example/main.go) for a complete working example. |
| 235 | + |
| 236 | +To run the example: |
| 237 | + |
| 238 | +```bash |
| 239 | +cd example |
| 240 | +go run main.go -name "node1" |
| 241 | +``` |
| 242 | + |
| 243 | +In another terminal: |
| 244 | + |
| 245 | +```bash |
| 246 | +cd example |
| 247 | +go run main.go -name "node2" |
| 248 | +``` |
| 249 | + |
| 250 | +The two nodes will discover each other and exchange messages. |
| 251 | + |
| 252 | +## How It Works |
| 253 | + |
| 254 | +1. **Peer Discovery**: The library uses multiple discovery mechanisms: |
| 255 | + - **DHT**: Connects to IPFS bootstrap peers and advertises topics on the distributed hash table |
| 256 | + - **mDNS**: Discovers peers on the local network |
| 257 | + - **Peer Cache**: Persists peer information to `peer_cache.json` for faster reconnection |
| 258 | + |
| 259 | +2. **NAT Traversal**: Automatically handles NAT traversal through: |
| 260 | + - **Hole Punching**: Attempts direct connections between NAT'd peers |
| 261 | + - **Relay**: Falls back to relay connections when direct connections fail |
| 262 | + - **UPnP/NAT-PMP**: Automatically configures port forwarding when possible |
| 263 | + |
| 264 | +3. **Message Routing**: Uses GossipSub for efficient topic-based message propagation |
| 265 | + |
| 266 | +## License |
| 267 | + |
| 268 | +MIT |
0 commit comments