Add NIP-11 relay synchronization and group management features
- 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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user