Add NIP-11 relay synchronization and group management features
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled

- Introduced a new `sync` package for managing NIP-11 relay information and relay group configurations.
- Implemented a cache for NIP-11 documents, allowing retrieval of relay public keys and authoritative configurations.
- Enhanced the sync manager to update peer lists based on authoritative configurations from relay group events.
- Updated event handling to incorporate policy checks during event imports, ensuring compliance with relay rules.
- Refactored various components to utilize the new `sha256-simd` package for improved performance.
- Added comprehensive tests to validate the new synchronization and group management functionalities.
- Bumped version to v0.24.1 to reflect these changes.
This commit is contained in:
2025-11-03 18:17:15 +00:00
parent e161d0e4be
commit e56bf76257
83 changed files with 3712 additions and 7417 deletions

View File

@@ -1,56 +1,368 @@
# NWC Client
Nostr Wallet Connect (NIP-47) client implementation.
Nostr Wallet Connect (NIP-47) client implementation for the ORLY relay. This package provides a complete client for connecting to NWC-compatible lightning wallets, enabling the relay to accept payments through the Nostr protocol.
## Features
- **NIP-47 Compliance**: Full implementation of the Nostr Wallet Connect specification
- **NIP-44 Encryption**: End-to-end encrypted communication with wallets
- **Payment Processing**: Invoice creation, payment, and balance checking
- **Real-time Notifications**: Subscribe to payment events and wallet updates
- **Error Handling**: Comprehensive error handling and recovery
- **Context Support**: Proper Go context integration for timeouts and cancellation
- **Thread Safety**: Concurrent access support with proper synchronization
## Installation
```bash
go get next.orly.dev/pkg/protocol/nwc
```
## Usage
### Basic Client Setup
```go
import "orly.dev/pkg/protocol/nwc"
import "next.orly.dev/pkg/protocol/nwc"
// Create client from NWC connection URI
client, err := nwc.NewClient("nostr+walletconnect://...")
if err != nil {
log.Fatal(err)
}
defer client.Close()
```
// Make requests
### Making Requests
```go
ctx := context.Background()
// Get wallet information
var info map[string]any
err = client.Request(ctx, "get_info", nil, &info)
if err != nil {
log.Printf("Failed to get info: %v", err)
}
// Get wallet balance
var balance map[string]any
err = client.Request(ctx, "get_balance", nil, &balance)
// Create invoice
params := map[string]any{
"amount": 1000, // msats
"description": "ORLY Relay Access",
}
var invoice map[string]any
params := map[string]any{"amount": 1000, "description": "test"}
err = client.Request(ctx, "make_invoice", params, &invoice)
// Check invoice status
lookupParams := map[string]any{
"payment_hash": "abc123...",
}
var status map[string]any
err = client.Request(ctx, "lookup_invoice", lookupParams, &status)
// Pay invoice
payParams := map[string]any{
"invoice": "lnbc10n1pj...",
}
var payment map[string]any
err = client.Request(ctx, "pay_invoice", payParams, &payment)
```
## Methods
### Payment Notifications
- `get_info` - Get wallet info
- `get_balance` - Get wallet balance
- `make_invoice` - Create invoice
- `lookup_invoice` - Check invoice status
- `pay_invoice` - Pay invoice
## Payment Notifications
Subscribe to real-time payment notifications:
```go
// Subscribe to payment notifications
err = client.SubscribeNotifications(ctx, func(notificationType string, notification map[string]any) error {
if notificationType == "payment_received" {
switch notificationType {
case "payment_received":
amount := notification["amount"].(float64)
paymentHash := notification["payment_hash"].(string)
description := notification["description"].(string)
log.Printf("Payment received: %.2f sats for %s", amount/1000, description)
// Process payment...
case "payment_sent":
amount := notification["amount"].(float64)
paymentHash := notification["payment_hash"].(string)
log.Printf("Payment sent: %.2f sats", amount/1000)
// Handle outgoing payment...
default:
log.Printf("Unknown notification type: %s", notificationType)
}
return nil
})
```
## Features
## API Reference
### Client Methods
#### `NewClient(uri string) (*Client, error)`
Creates a new NWC client from a connection URI.
#### `Request(ctx context.Context, method string, params, result any) error`
Makes a synchronous request to the wallet.
#### `SubscribeNotifications(ctx context.Context, handler NotificationHandler) error`
Subscribes to payment notifications with the provided handler function.
#### `Close() error`
Closes the client and cleans up resources.
### Supported Methods
| Method | Parameters | Description |
|--------|------------|-------------|
| `get_info` | None | Get wallet information and supported methods |
| `get_balance` | None | Get current wallet balance |
| `make_invoice` | `amount`, `description` | Create a new lightning invoice |
| `lookup_invoice` | `payment_hash` | Check status of an existing invoice |
| `pay_invoice` | `invoice` | Pay a lightning invoice |
### Notification Types
- `payment_received`: Incoming payment received
- `payment_sent`: Outgoing payment sent
- `invoice_paid`: Invoice was paid (alternative to payment_received)
## Integration with ORLY
The NWC client is integrated into the ORLY relay for payment processing:
```bash
# Enable NWC payments
export ORLY_NWC_URI="nostr+walletconnect://..."
# Start relay with payment support
./orly
```
The relay will automatically:
- Create invoices for premium features
- Validate payments before granting access
- Handle payment notifications
- Update user balances
## Error Handling
The client provides comprehensive error handling:
```go
err = client.Request(ctx, "make_invoice", params, &result)
if err != nil {
var nwcErr *nwc.Error
if errors.As(err, &nwcErr) {
switch nwcErr.Code {
case nwc.ErrInsufficientBalance:
// Handle insufficient funds
case nwc.ErrInvoiceExpired:
// Handle expired invoice
default:
// Handle other errors
}
}
}
```
## Security Considerations
- **URI Protection**: Keep NWC URIs secure and don't log them
- **Context Timeouts**: Always use context with timeouts for requests
- **Error Sanitization**: Don't expose internal wallet errors to users
- **Rate Limiting**: Implement rate limiting to prevent abuse
- **Audit Logging**: Log payment operations for audit purposes
## Testing
The NWC client includes comprehensive tests:
### Running Tests
```bash
# Run NWC package tests
go test ./pkg/protocol/nwc
# Run with verbose output
go test -v ./pkg/protocol/nwc
# Run integration tests (requires wallet connection)
go test -tags=integration ./pkg/protocol/nwc
```
### Integration Testing
Part of the full test suite:
```bash
# Run all tests including NWC
./scripts/test.sh
# Run protocol package tests
go test ./pkg/protocol/...
```
### Test Coverage
Tests verify:
- Client creation and connection
- Request/response handling
- Notification subscriptions
- Error conditions and recovery
- NIP-44 encryption
- Event signing
- Relay communication
- Payment notifications
- Error handling
- Concurrent access patterns
- Context cancellation
### Mock Testing
For testing without a real wallet:
```go
// Create mock client for testing
mockClient := nwc.NewMockClient()
mockClient.SetBalance(1000000) // 1000 sats
// Use in tests
result := make(map[string]any)
err := mockClient.Request(ctx, "get_balance", nil, &result)
```
## Examples
### Complete Payment Flow
```go
package main
import (
"context"
"log"
"time"
"next.orly.dev/pkg/protocol/nwc"
)
func main() {
client, err := nwc.NewClient("nostr+walletconnect://...")
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Get wallet info
var info map[string]any
if err := client.Request(ctx, "get_info", nil, &info); err != nil {
log.Printf("Failed to get info: %v", err)
return
}
log.Printf("Connected to wallet: %v", info)
// Create invoice
invoiceParams := map[string]any{
"amount": 5000, // 5 sats
"description": "Test payment",
}
var invoice map[string]any
if err := client.Request(ctx, "make_invoice", invoiceParams, &invoice); err != nil {
log.Printf("Failed to create invoice: %v", err)
return
}
bolt11 := invoice["invoice"].(string)
log.Printf("Created invoice: %s", bolt11)
// In a real application, you would present this invoice to the user
// For testing, you can pay it using the same client
payParams := map[string]any{"invoice": bolt11}
var payment map[string]any
if err := client.Request(ctx, "pay_invoice", payParams, &payment); err != nil {
log.Printf("Failed to pay invoice: %v", err)
return
}
log.Printf("Payment successful: %v", payment)
}
```
### Notification Handler
```go
func setupPaymentHandler(client *nwc.Client) {
ctx := context.Background()
err := client.SubscribeNotifications(ctx, func(notificationType string, notification map[string]any) error {
log.Printf("Received notification: %s", notificationType)
switch notificationType {
case "payment_received":
// Grant access to paid feature
userID := notification["description"].(string)
amount := notification["amount"].(float64)
grantPremiumAccess(userID, amount)
case "payment_sent":
// Log outgoing payment
amount := notification["amount"].(float64)
log.Printf("Outgoing payment: %.2f sats", amount/1000)
default:
log.Printf("Unknown notification type: %s", notificationType)
}
return nil
})
if err != nil {
log.Printf("Failed to subscribe to notifications: %v", err)
}
}
```
## Development
### Building
```bash
go build ./pkg/protocol/nwc
```
### Code Quality
- Comprehensive error handling
- Go context support
- Thread-safe operations
- Extensive test coverage
- Proper resource cleanup
## Troubleshooting
### Common Issues
1. **Connection Failed**: Check NWC URI format and wallet availability
2. **Timeout Errors**: Use context with appropriate timeouts
3. **Encryption Errors**: Verify NIP-44 implementation compatibility
4. **Notification Issues**: Check wallet support for notifications
### Debugging
Enable debug logging:
```bash
export ORLY_LOG_LEVEL=debug
```
Monitor relay logs for NWC operations.
## License
Part of the next.orly.dev project. See main LICENSE file.