Add wallet service implementation and mock CLI tool
- pkg/protocol/nwc/wallet.go - Implemented `WalletService` with method registration and request handling. - Added default stub handlers for supported wallet methods. - Included support for notifications with `SendNotification`. - pkg/protocol/nwc/client-methods.go - Added `Subscribe` function for handling client subscriptions. - cmd/walletcli/mock-wallet-service/main.go - Implemented a mock CLI tool for wallet service. - Added command-line flags for relay connection and key management. - Added handlers for various wallet service methods (e.g., `GetInfo`, `GetBalance`, etc.). - pkg/protocol/nwc/types.go - Added `GetWalletServiceInfo` to the list of wallet service capabilities.
This commit is contained in:
@@ -106,8 +106,59 @@ func handleGetWalletServiceInfo(c context.T, cl *nwc.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetInfo(c context.T, cl *nwc.Client) {
|
||||
if _, raw, err := cl.GetInfo(c, true); !chk.E(err) {
|
||||
func handleCancelHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> cancel_hold_invoice <payment_hash>")
|
||||
return
|
||||
}
|
||||
|
||||
params := &nwc.CancelHoldInvoiceParams{
|
||||
PaymentHash: args[0],
|
||||
}
|
||||
var err error
|
||||
var raw []byte
|
||||
if raw, err = cl.CancelHoldInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleCreateConnection(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 3 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> create_connection <pubkey> <name> <methods> [<notification_types>] [<max_amount>] [<budget_renewal>] [<expires_at>]")
|
||||
return
|
||||
}
|
||||
params := &nwc.CreateConnectionParams{
|
||||
Pubkey: args[0],
|
||||
Name: args[1],
|
||||
RequestMethods: strings.Split(args[2], ","),
|
||||
}
|
||||
if len(args) > 3 {
|
||||
params.NotificationTypes = strings.Split(args[3], ",")
|
||||
}
|
||||
if len(args) > 4 {
|
||||
maxAmount, err := strconv.ParseUint(args[4], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing max_amount: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.MaxAmount = &maxAmount
|
||||
}
|
||||
if len(args) > 5 {
|
||||
params.BudgetRenewal = &args[5]
|
||||
}
|
||||
if len(args) > 6 {
|
||||
expiresAt, err := strconv.ParseInt(args[6], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing expires_at: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.ExpiresAt = &expiresAt
|
||||
}
|
||||
var raw []byte
|
||||
var err error
|
||||
if raw, err = cl.CreateConnection(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
@@ -124,86 +175,8 @@ func handleGetBudget(c context.T, cl *nwc.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleMakeInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> make_invoice <amount> [<description>] [<description_hash>] [<expiry>]")
|
||||
return
|
||||
}
|
||||
amount, err := strconv.ParseUint(args[0], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing amount: %v\n", err)
|
||||
return
|
||||
}
|
||||
params := &nwc.MakeInvoiceParams{
|
||||
Amount: amount,
|
||||
}
|
||||
if len(args) > 1 {
|
||||
params.Description = args[1]
|
||||
}
|
||||
if len(args) > 2 {
|
||||
params.DescriptionHash = args[2]
|
||||
}
|
||||
if len(args) > 3 {
|
||||
expiry, err := strconv.ParseInt(args[3], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing expiry: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.Expiry = &expiry
|
||||
}
|
||||
var raw []byte
|
||||
if _, raw, err = cl.MakeInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handlePayInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> pay_invoice <invoice> [<amount>] [<comment>]")
|
||||
return
|
||||
}
|
||||
params := &nwc.PayInvoiceParams{
|
||||
Invoice: args[0],
|
||||
}
|
||||
if len(args) > 1 {
|
||||
amount, err := strconv.ParseUint(args[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing amount: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.Amount = &amount
|
||||
}
|
||||
if len(args) > 2 {
|
||||
comment := args[2]
|
||||
params.Metadata = &nwc.PayInvoiceMetadata{
|
||||
Comment: &comment,
|
||||
}
|
||||
}
|
||||
if _, raw, err := cl.PayInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleLookupInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> lookup_invoice <payment_hash or invoice>")
|
||||
return
|
||||
}
|
||||
params := &nwc.LookupInvoiceParams{}
|
||||
// Determine if the argument is a payment hash or an invoice
|
||||
if strings.HasPrefix(args[0], "ln") {
|
||||
invoice := args[0]
|
||||
params.Invoice = &invoice
|
||||
} else {
|
||||
paymentHash := args[0]
|
||||
params.PaymentHash = &paymentHash
|
||||
}
|
||||
var err error
|
||||
var raw []byte
|
||||
if _, raw, err = cl.LookupInvoice(c, params, true); !chk.E(err) {
|
||||
func handleGetInfo(c context.T, cl *nwc.Client) {
|
||||
if _, raw, err := cl.GetInfo(c, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
@@ -251,6 +224,28 @@ func handleListTransactions(c context.T, cl *nwc.Client, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleLookupInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> lookup_invoice <payment_hash or invoice>")
|
||||
return
|
||||
}
|
||||
params := &nwc.LookupInvoiceParams{}
|
||||
// Determine if the argument is a payment hash or an invoice
|
||||
if strings.HasPrefix(args[0], "ln") {
|
||||
invoice := args[0]
|
||||
params.Invoice = &invoice
|
||||
} else {
|
||||
paymentHash := args[0]
|
||||
params.PaymentHash = &paymentHash
|
||||
}
|
||||
var err error
|
||||
var raw []byte
|
||||
if _, raw, err = cl.LookupInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMakeHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 2 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
@@ -286,52 +281,36 @@ func handleMakeHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleSettleHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
func handleMakeInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> settle_hold_invoice <preimage>")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> make_invoice <amount> [<description>] [<description_hash>] [<expiry>]")
|
||||
return
|
||||
}
|
||||
params := &nwc.SettleHoldInvoiceParams{
|
||||
Preimage: args[0],
|
||||
}
|
||||
var raw []byte
|
||||
var err error
|
||||
if raw, err = cl.SettleHoldInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleCancelHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> cancel_hold_invoice <payment_hash>")
|
||||
amount, err := strconv.ParseUint(args[0], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing amount: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
params := &nwc.CancelHoldInvoiceParams{
|
||||
PaymentHash: args[0],
|
||||
params := &nwc.MakeInvoiceParams{
|
||||
Amount: amount,
|
||||
}
|
||||
var err error
|
||||
var raw []byte
|
||||
if raw, err = cl.CancelHoldInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
if len(args) > 1 {
|
||||
params.Description = args[1]
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignMessage(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> sign_message <message>")
|
||||
return
|
||||
if len(args) > 2 {
|
||||
params.DescriptionHash = args[2]
|
||||
}
|
||||
|
||||
params := &nwc.SignMessageParams{
|
||||
Message: args[0],
|
||||
if len(args) > 3 {
|
||||
expiry, err := strconv.ParseInt(args[3], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing expiry: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.Expiry = &expiry
|
||||
}
|
||||
var raw []byte
|
||||
var err error
|
||||
if _, raw, err = cl.SignMessage(c, params, true); !chk.E(err) {
|
||||
if _, raw, err = cl.MakeInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
@@ -381,42 +360,63 @@ func handlePayKeysend(c context.T, cl *nwc.Client, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleCreateConnection(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 3 {
|
||||
func handlePayInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> create_connection <pubkey> <name> <methods> [<notification_types>] [<max_amount>] [<budget_renewal>] [<expires_at>]")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> pay_invoice <invoice> [<amount>] [<comment>]")
|
||||
return
|
||||
}
|
||||
params := &nwc.CreateConnectionParams{
|
||||
Pubkey: args[0],
|
||||
Name: args[1],
|
||||
RequestMethods: strings.Split(args[2], ","),
|
||||
params := &nwc.PayInvoiceParams{
|
||||
Invoice: args[0],
|
||||
}
|
||||
if len(args) > 3 {
|
||||
params.NotificationTypes = strings.Split(args[3], ",")
|
||||
}
|
||||
if len(args) > 4 {
|
||||
maxAmount, err := strconv.ParseUint(args[4], 10, 64)
|
||||
if len(args) > 1 {
|
||||
amount, err := strconv.ParseUint(args[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing max_amount: %v\n", err)
|
||||
fmt.Printf("Error parsing amount: %v\n", err)
|
||||
return
|
||||
}
|
||||
params.MaxAmount = &maxAmount
|
||||
params.Amount = &amount
|
||||
}
|
||||
if len(args) > 5 {
|
||||
params.BudgetRenewal = &args[5]
|
||||
}
|
||||
if len(args) > 6 {
|
||||
expiresAt, err := strconv.ParseInt(args[6], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing expires_at: %v\n", err)
|
||||
return
|
||||
if len(args) > 2 {
|
||||
comment := args[2]
|
||||
params.Metadata = &nwc.PayInvoiceMetadata{
|
||||
Comment: &comment,
|
||||
}
|
||||
params.ExpiresAt = &expiresAt
|
||||
}
|
||||
if _, raw, err := cl.PayInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleSettleHoldInvoice(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> settle_hold_invoice <preimage>")
|
||||
return
|
||||
}
|
||||
params := &nwc.SettleHoldInvoiceParams{
|
||||
Preimage: args[0],
|
||||
}
|
||||
var raw []byte
|
||||
var err error
|
||||
if raw, err = cl.CreateConnection(c, params, true); !chk.E(err) {
|
||||
if raw, err = cl.SettleHoldInvoice(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignMessage(c context.T, cl *nwc.Client, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Error: Missing required arguments")
|
||||
fmt.Println("Usage: walletcli <NWC connection URL> sign_message <message>")
|
||||
return
|
||||
}
|
||||
|
||||
params := &nwc.SignMessageParams{
|
||||
Message: args[0],
|
||||
}
|
||||
var raw []byte
|
||||
var err error
|
||||
if _, raw, err = cl.SignMessage(c, params, true); !chk.E(err) {
|
||||
fmt.Println(string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
456
cmd/walletcli/mock-wallet-service/main.go
Normal file
456
cmd/walletcli/mock-wallet-service/main.go
Normal file
@@ -0,0 +1,456 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"orly.dev/pkg/crypto/encryption"
|
||||
"orly.dev/pkg/crypto/p256k"
|
||||
"orly.dev/pkg/encoders/event"
|
||||
"orly.dev/pkg/encoders/filter"
|
||||
"orly.dev/pkg/encoders/filters"
|
||||
"orly.dev/pkg/encoders/hex"
|
||||
"orly.dev/pkg/encoders/kind"
|
||||
"orly.dev/pkg/encoders/kinds"
|
||||
"orly.dev/pkg/encoders/tag"
|
||||
"orly.dev/pkg/encoders/tags"
|
||||
"orly.dev/pkg/encoders/timestamp"
|
||||
"orly.dev/pkg/interfaces/signer"
|
||||
"orly.dev/pkg/protocol/nwc"
|
||||
"orly.dev/pkg/protocol/ws"
|
||||
"orly.dev/pkg/utils/chk"
|
||||
"orly.dev/pkg/utils/context"
|
||||
"orly.dev/pkg/utils/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
relayURL = flag.String("relay", "ws://localhost:8080", "Relay URL to connect to")
|
||||
walletKey = flag.String("key", "", "Wallet private key (hex)")
|
||||
generateKey = flag.Bool("generate-key", false, "Generate a new wallet key")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Create context
|
||||
c, cancel := context.Cancel(context.Bg())
|
||||
interrupt.AddHandler(cancel)
|
||||
defer cancel()
|
||||
|
||||
// Initialize wallet key
|
||||
var walletSigner signer.I
|
||||
var err error
|
||||
|
||||
if *generateKey {
|
||||
// Generate a new wallet key
|
||||
walletSigner = &p256k.Signer{}
|
||||
if err = walletSigner.Generate(); chk.E(err) {
|
||||
fmt.Printf("Error generating wallet key: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Generated wallet key: %s\n", hex.Enc(walletSigner.Sec()))
|
||||
fmt.Printf("Wallet public key: %s\n", hex.Enc(walletSigner.Pub()))
|
||||
} else if *walletKey != "" {
|
||||
// Use provided wallet key
|
||||
if walletSigner, err = p256k.NewSecFromHex(*walletKey); chk.E(err) {
|
||||
fmt.Printf("Error initializing wallet key: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Using wallet key: %s\n", *walletKey)
|
||||
fmt.Printf("Wallet public key: %s\n", hex.Enc(walletSigner.Pub()))
|
||||
} else {
|
||||
// Generate a temporary wallet key
|
||||
walletSigner = &p256k.Signer{}
|
||||
if err = walletSigner.Generate(); chk.E(err) {
|
||||
fmt.Printf("Error generating temporary wallet key: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Generated temporary wallet key: %s\n", hex.Enc(walletSigner.Sec()))
|
||||
fmt.Printf("Wallet public key: %s\n", hex.Enc(walletSigner.Pub()))
|
||||
}
|
||||
|
||||
// Connect to relay
|
||||
fmt.Printf("Connecting to relay: %s\n", *relayURL)
|
||||
relay, err := ws.RelayConnect(c, *relayURL)
|
||||
if err != nil {
|
||||
fmt.Printf("Error connecting to relay: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer relay.Close()
|
||||
fmt.Println("Connected to relay")
|
||||
|
||||
// Create a mock wallet service info event
|
||||
walletServiceInfoEvent := createWalletServiceInfoEvent(walletSigner)
|
||||
|
||||
// Publish wallet service info event
|
||||
if err = relay.Publish(c, walletServiceInfoEvent); chk.E(err) {
|
||||
fmt.Printf("Error publishing wallet service info: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("Published wallet service info")
|
||||
|
||||
// Subscribe to wallet requests
|
||||
fmt.Println("Subscribing to wallet requests...")
|
||||
sub, err := relay.Subscribe(
|
||||
c, filters.New(
|
||||
&filter.F{
|
||||
Kinds: kinds.New(kind.WalletRequest),
|
||||
Tags: tags.New(tag.New("#p", hex.Enc(walletSigner.Pub()))),
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("Error subscribing to wallet requests: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer sub.Unsub()
|
||||
fmt.Println("Subscribed to wallet requests")
|
||||
|
||||
// Process wallet requests
|
||||
fmt.Println("Waiting for wallet requests...")
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
fmt.Println("Context canceled, exiting")
|
||||
return
|
||||
case ev := <-sub.Events:
|
||||
fmt.Printf("Received wallet request: %s\n", hex.Enc(ev.ID))
|
||||
go handleWalletRequest(c, relay, walletSigner, ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleWalletRequest processes a wallet request and sends a response
|
||||
func handleWalletRequest(c context.T, relay *ws.Client, walletKey signer.I, ev *event.E) {
|
||||
// Get the client's public key from the event
|
||||
clientPubKey := ev.Pubkey
|
||||
|
||||
// Generate conversation key
|
||||
var ck []byte
|
||||
var err error
|
||||
if ck, err = encryption.GenerateConversationKeyWithSigner(
|
||||
walletKey,
|
||||
clientPubKey,
|
||||
); chk.E(err) {
|
||||
fmt.Printf("Error generating conversation key: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt the content
|
||||
var content []byte
|
||||
if content, err = encryption.Decrypt(ev.Content, ck); chk.E(err) {
|
||||
fmt.Printf("Error decrypting content: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the request
|
||||
var req nwc.Request
|
||||
if err = json.Unmarshal(content, &req); chk.E(err) {
|
||||
fmt.Printf("Error parsing request: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Handling method: %s\n", req.Method)
|
||||
|
||||
// Process the request based on the method
|
||||
var result interface{}
|
||||
var respErr *nwc.ResponseError
|
||||
|
||||
switch req.Method {
|
||||
case string(nwc.GetWalletServiceInfo):
|
||||
result = handleGetWalletServiceInfo()
|
||||
case string(nwc.GetInfo):
|
||||
result = handleGetInfo(walletKey)
|
||||
case string(nwc.GetBalance):
|
||||
result = handleGetBalance()
|
||||
case string(nwc.GetBudget):
|
||||
result = handleGetBudget()
|
||||
case string(nwc.MakeInvoice):
|
||||
result = handleMakeInvoice()
|
||||
case string(nwc.PayInvoice):
|
||||
result = handlePayInvoice()
|
||||
case string(nwc.PayKeysend):
|
||||
result = handlePayKeysend()
|
||||
case string(nwc.LookupInvoice):
|
||||
result = handleLookupInvoice()
|
||||
case string(nwc.ListTransactions):
|
||||
result = handleListTransactions()
|
||||
case string(nwc.MakeHoldInvoice):
|
||||
result = handleMakeHoldInvoice()
|
||||
case string(nwc.SettleHoldInvoice):
|
||||
// No result for SettleHoldInvoice
|
||||
case string(nwc.CancelHoldInvoice):
|
||||
// No result for CancelHoldInvoice
|
||||
case string(nwc.SignMessage):
|
||||
result = handleSignMessage()
|
||||
case string(nwc.CreateConnection):
|
||||
// No result for CreateConnection
|
||||
default:
|
||||
respErr = &nwc.ResponseError{
|
||||
Code: "method_not_found",
|
||||
Message: fmt.Sprintf("method %s not found", req.Method),
|
||||
}
|
||||
}
|
||||
|
||||
// Create response
|
||||
resp := nwc.Response{
|
||||
ResultType: req.Method,
|
||||
Result: result,
|
||||
Error: respErr,
|
||||
}
|
||||
|
||||
// Marshal response
|
||||
var respBytes []byte
|
||||
if respBytes, err = json.Marshal(resp); chk.E(err) {
|
||||
fmt.Printf("Error marshaling response: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Encrypt response
|
||||
var encResp []byte
|
||||
if encResp, err = encryption.Encrypt(respBytes, ck); chk.E(err) {
|
||||
fmt.Printf("Error encrypting response: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create response event
|
||||
respEv := &event.E{
|
||||
Content: encResp,
|
||||
CreatedAt: timestamp.Now(),
|
||||
Kind: kind.WalletResponse,
|
||||
Tags: tags.New(
|
||||
tag.New("p", hex.Enc(clientPubKey)),
|
||||
tag.New("e", hex.Enc(ev.ID)),
|
||||
tag.New(string(nwc.EncryptionTag), string(nwc.Nip44V2)),
|
||||
),
|
||||
}
|
||||
|
||||
// Sign the response event
|
||||
if err = respEv.Sign(walletKey); chk.E(err) {
|
||||
fmt.Printf("Error signing response event: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Publish the response event
|
||||
if err = relay.Publish(c, respEv); chk.E(err) {
|
||||
fmt.Printf("Error publishing response event: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully handled request: %s\n", hex.Enc(ev.ID))
|
||||
}
|
||||
|
||||
// createWalletServiceInfoEvent creates a wallet service info event
|
||||
func createWalletServiceInfoEvent(walletKey signer.I) *event.E {
|
||||
ev := &event.E{
|
||||
Content: []byte(
|
||||
string(nwc.GetWalletServiceInfo) + " " +
|
||||
string(nwc.GetInfo) + " " +
|
||||
string(nwc.GetBalance) + " " +
|
||||
string(nwc.GetBudget) + " " +
|
||||
string(nwc.MakeInvoice) + " " +
|
||||
string(nwc.PayInvoice) + " " +
|
||||
string(nwc.PayKeysend) + " " +
|
||||
string(nwc.LookupInvoice) + " " +
|
||||
string(nwc.ListTransactions) + " " +
|
||||
string(nwc.MakeHoldInvoice) + " " +
|
||||
string(nwc.SettleHoldInvoice) + " " +
|
||||
string(nwc.CancelHoldInvoice) + " " +
|
||||
string(nwc.SignMessage) + " " +
|
||||
string(nwc.CreateConnection),
|
||||
),
|
||||
CreatedAt: timestamp.Now(),
|
||||
Kind: kind.WalletServiceInfo,
|
||||
Tags: tags.New(
|
||||
tag.New(string(nwc.EncryptionTag), string(nwc.Nip44V2)),
|
||||
tag.New(string(nwc.NotificationTag), string(nwc.PaymentReceived)+" "+string(nwc.PaymentSent)+" "+string(nwc.HoldInvoiceAccepted)),
|
||||
),
|
||||
}
|
||||
if err := ev.Sign(walletKey); chk.E(err) {
|
||||
fmt.Printf("Error signing wallet service info event: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return ev
|
||||
}
|
||||
|
||||
// Handler functions for each method
|
||||
|
||||
func handleGetWalletServiceInfo() *nwc.WalletServiceInfo {
|
||||
fmt.Println("Handling GetWalletServiceInfo request")
|
||||
return &nwc.WalletServiceInfo{
|
||||
EncryptionTypes: []nwc.EncryptionType{nwc.Nip44V2},
|
||||
Capabilities: []nwc.Capability{
|
||||
nwc.GetWalletServiceInfo,
|
||||
nwc.GetInfo,
|
||||
nwc.GetBalance,
|
||||
nwc.GetBudget,
|
||||
nwc.MakeInvoice,
|
||||
nwc.PayInvoice,
|
||||
nwc.PayKeysend,
|
||||
nwc.LookupInvoice,
|
||||
nwc.ListTransactions,
|
||||
nwc.MakeHoldInvoice,
|
||||
nwc.SettleHoldInvoice,
|
||||
nwc.CancelHoldInvoice,
|
||||
nwc.SignMessage,
|
||||
nwc.CreateConnection,
|
||||
},
|
||||
NotificationTypes: []nwc.NotificationType{
|
||||
nwc.PaymentReceived,
|
||||
nwc.PaymentSent,
|
||||
nwc.HoldInvoiceAccepted,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetInfo(walletKey signer.I) *nwc.GetInfoResult {
|
||||
fmt.Println("Handling GetInfo request")
|
||||
return &nwc.GetInfoResult{
|
||||
Alias: "Mock Wallet",
|
||||
Color: "#ff9900",
|
||||
Pubkey: hex.Enc(walletKey.Pub()),
|
||||
Network: "testnet",
|
||||
BlockHeight: 123456,
|
||||
BlockHash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
||||
Methods: []string{
|
||||
string(nwc.GetWalletServiceInfo),
|
||||
string(nwc.GetInfo),
|
||||
string(nwc.GetBalance),
|
||||
string(nwc.GetBudget),
|
||||
string(nwc.MakeInvoice),
|
||||
string(nwc.PayInvoice),
|
||||
string(nwc.PayKeysend),
|
||||
string(nwc.LookupInvoice),
|
||||
string(nwc.ListTransactions),
|
||||
string(nwc.MakeHoldInvoice),
|
||||
string(nwc.SettleHoldInvoice),
|
||||
string(nwc.CancelHoldInvoice),
|
||||
string(nwc.SignMessage),
|
||||
string(nwc.CreateConnection),
|
||||
},
|
||||
Notifications: []string{
|
||||
string(nwc.PaymentReceived),
|
||||
string(nwc.PaymentSent),
|
||||
string(nwc.HoldInvoiceAccepted),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetBalance() *nwc.GetBalanceResult {
|
||||
fmt.Println("Handling GetBalance request")
|
||||
return &nwc.GetBalanceResult{
|
||||
Balance: 1000000, // 1,000,000 sats
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetBudget() *nwc.GetBudgetResult {
|
||||
fmt.Println("Handling GetBudget request")
|
||||
return &nwc.GetBudgetResult{
|
||||
UsedBudget: 5000,
|
||||
TotalBudget: 10000,
|
||||
RenewsAt: int(time.Now().Add(24 * time.Hour).Unix()),
|
||||
RenewalPeriod: "daily",
|
||||
}
|
||||
}
|
||||
|
||||
func handleMakeInvoice() *nwc.Transaction {
|
||||
fmt.Println("Handling MakeInvoice request")
|
||||
return &nwc.Transaction{
|
||||
Type: "invoice",
|
||||
State: "unpaid",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Mock invoice",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour).Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func handlePayInvoice() *nwc.PayInvoiceResult {
|
||||
fmt.Println("Handling PayInvoice request")
|
||||
return &nwc.PayInvoiceResult{
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
FeesPaid: 10,
|
||||
}
|
||||
}
|
||||
|
||||
func handlePayKeysend() *nwc.PayKeysendResult {
|
||||
fmt.Println("Handling PayKeysend request")
|
||||
return &nwc.PayKeysendResult{
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
FeesPaid: 5,
|
||||
}
|
||||
}
|
||||
|
||||
func handleLookupInvoice() *nwc.Transaction {
|
||||
fmt.Println("Handling LookupInvoice request")
|
||||
return &nwc.Transaction{
|
||||
Type: "invoice",
|
||||
State: "settled",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Mock invoice",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Add(-1 * time.Hour).Unix(),
|
||||
ExpiresAt: time.Now().Add(23 * time.Hour).Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func handleListTransactions() *nwc.ListTransactionsResult {
|
||||
fmt.Println("Handling ListTransactions request")
|
||||
return &nwc.ListTransactionsResult{
|
||||
Transactions: []nwc.Transaction{
|
||||
{
|
||||
Type: "incoming",
|
||||
State: "settled",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Mock incoming transaction",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour).Unix(),
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
|
||||
},
|
||||
{
|
||||
Type: "outgoing",
|
||||
State: "settled",
|
||||
Invoice: "lnbc20n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Mock outgoing transaction",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 2000,
|
||||
FeesPaid: 10,
|
||||
CreatedAt: time.Now().Add(-12 * time.Hour).Unix(),
|
||||
ExpiresAt: time.Now().Add(36 * time.Hour).Unix(),
|
||||
},
|
||||
},
|
||||
TotalCount: 2,
|
||||
}
|
||||
}
|
||||
|
||||
func handleMakeHoldInvoice() *nwc.Transaction {
|
||||
fmt.Println("Handling MakeHoldInvoice request")
|
||||
return &nwc.Transaction{
|
||||
Type: "hold_invoice",
|
||||
State: "unpaid",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Mock hold invoice",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour).Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignMessage() *nwc.SignMessageResult {
|
||||
fmt.Println("Handling SignMessage request")
|
||||
return &nwc.SignMessageResult{
|
||||
Message: "Mock message",
|
||||
Signature: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"orly.dev/pkg/encoders/event"
|
||||
"orly.dev/pkg/encoders/filter"
|
||||
"orly.dev/pkg/encoders/filters"
|
||||
"orly.dev/pkg/encoders/kind"
|
||||
@@ -172,3 +173,36 @@ func (cl *Client) SignMessage(
|
||||
raw, err = cl.RPC(c, SignMessage, sm, &res, noUnmarshal, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) Subscribe(c context.T) (evc event.C, err error) {
|
||||
var rc *ws.Client
|
||||
if rc, err = ws.RelayConnect(c, cl.relay); chk.E(err) {
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
var sub *ws.Subscription
|
||||
if sub, err = rc.Subscribe(
|
||||
c, filters.New(
|
||||
&filter.F{
|
||||
Kinds: kinds.New(
|
||||
kind.WalletNotification, kind.WalletNotificationNip4,
|
||||
),
|
||||
Authors: tag.New(cl.walletPublicKey),
|
||||
},
|
||||
),
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
defer sub.Unsub()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return
|
||||
case ev := <-sub.Events:
|
||||
evc <- ev
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
@@ -157,36 +157,3 @@ func (cl *Client) RPC(
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) Subscribe(c context.T) (evc event.C, err error) {
|
||||
var rc *ws.Client
|
||||
if rc, err = ws.RelayConnect(c, cl.relay); chk.E(err) {
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
var sub *ws.Subscription
|
||||
if sub, err = rc.Subscribe(
|
||||
c, filters.New(
|
||||
&filter.F{
|
||||
Kinds: kinds.New(
|
||||
kind.WalletNotification, kind.WalletNotificationNip4,
|
||||
),
|
||||
Authors: tag.New(cl.walletPublicKey),
|
||||
},
|
||||
),
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
defer sub.Unsub()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.Done():
|
||||
return
|
||||
case ev := <-sub.Events:
|
||||
evc <- ev
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
943
pkg/protocol/nwc/handlers_test.go
Normal file
943
pkg/protocol/nwc/handlers_test.go
Normal file
@@ -0,0 +1,943 @@
|
||||
package nwc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"orly.dev/pkg/utils/context"
|
||||
)
|
||||
|
||||
// TestHandleGetWalletServiceInfo tests the handleGetWalletServiceInfo function
|
||||
func TestHandleGetWalletServiceInfo(t *testing.T) {
|
||||
// Create a handler function that returns a predefined WalletServiceInfo
|
||||
handler := func(c context.T, params json.RawMessage) (
|
||||
result interface{}, err error,
|
||||
) {
|
||||
return &WalletServiceInfo{
|
||||
EncryptionTypes: []EncryptionType{Nip44V2},
|
||||
Capabilities: []Capability{
|
||||
GetWalletServiceInfo,
|
||||
GetInfo,
|
||||
GetBalance,
|
||||
GetBudget,
|
||||
MakeInvoice,
|
||||
PayInvoice,
|
||||
},
|
||||
NotificationTypes: []NotificationType{
|
||||
PaymentReceived,
|
||||
PaymentSent,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get wallet service info: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
wsi, ok := result.(*WalletServiceInfo)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a WalletServiceInfo")
|
||||
}
|
||||
|
||||
// Check encryption types
|
||||
if len(wsi.EncryptionTypes) != 1 || string(wsi.EncryptionTypes[0]) != string(Nip44V2) {
|
||||
t.Errorf(
|
||||
"Expected encryption type %s, got %v", Nip44V2, wsi.EncryptionTypes,
|
||||
)
|
||||
}
|
||||
|
||||
// Check capabilities
|
||||
expectedCapabilities := []Capability{
|
||||
GetWalletServiceInfo,
|
||||
GetInfo,
|
||||
GetBalance,
|
||||
GetBudget,
|
||||
MakeInvoice,
|
||||
PayInvoice,
|
||||
}
|
||||
if len(wsi.Capabilities) != len(expectedCapabilities) {
|
||||
t.Errorf(
|
||||
"Expected %d capabilities, got %d", len(expectedCapabilities),
|
||||
len(wsi.Capabilities),
|
||||
)
|
||||
}
|
||||
|
||||
// Check notification types
|
||||
expectedNotificationTypes := []NotificationType{
|
||||
PaymentReceived,
|
||||
PaymentSent,
|
||||
}
|
||||
if len(wsi.NotificationTypes) != len(expectedNotificationTypes) {
|
||||
t.Errorf(
|
||||
"Expected %d notification types, got %d",
|
||||
len(expectedNotificationTypes), len(wsi.NotificationTypes),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleCancelHoldInvoice tests the handleCancelHoldInvoice function
|
||||
func TestHandleCancelHoldInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &CancelHoldInvoiceParams{
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}
|
||||
|
||||
// Create a handler function that processes the parameters
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p CancelHoldInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.PaymentHash != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid payment hash",
|
||||
}
|
||||
}
|
||||
|
||||
// Return nil result (success with no data)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to cancel hold invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result is nil (success with no data)
|
||||
if result != nil {
|
||||
t.Errorf("Expected nil result, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleCreateConnection tests the handleCreateConnection function
|
||||
func TestHandleCreateConnection(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &CreateConnectionParams{
|
||||
Pubkey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Name: "Test Connection",
|
||||
RequestMethods: []string{"get_info", "get_balance", "make_invoice"},
|
||||
NotificationTypes: []string{"payment_received", "payment_sent"},
|
||||
}
|
||||
|
||||
// Create a handler function that processes the parameters
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p CreateConnectionParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Pubkey != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid pubkey",
|
||||
}
|
||||
}
|
||||
if p.Name != "Test Connection" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid name",
|
||||
}
|
||||
}
|
||||
if len(p.RequestMethods) != 3 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid request methods",
|
||||
}
|
||||
}
|
||||
if len(p.NotificationTypes) != 2 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid notification types",
|
||||
}
|
||||
}
|
||||
|
||||
// Return nil result (success with no data)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create connection: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result is nil (success with no data)
|
||||
if result != nil {
|
||||
t.Errorf("Expected nil result, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleGetBalance tests the handleGetBalance function
|
||||
func TestHandleGetBalance(t *testing.T) {
|
||||
// Create a handler function that returns a predefined GetBalanceResult
|
||||
handler := func(c context.T, params json.RawMessage) (
|
||||
result interface{}, err error,
|
||||
) {
|
||||
return &GetBalanceResult{
|
||||
Balance: 1000000, // 1,000,000 sats
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get balance: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
balance, ok := result.(*GetBalanceResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a GetBalanceResult")
|
||||
}
|
||||
|
||||
// Check balance
|
||||
if balance.Balance != 1000000 {
|
||||
t.Errorf("Expected balance 1000000, got %d", balance.Balance)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleGetBudget tests the handleGetBudget function
|
||||
func TestHandleGetBudget(t *testing.T) {
|
||||
// Create a handler function that returns a predefined GetBudgetResult
|
||||
handler := func(c context.T, params json.RawMessage) (
|
||||
result interface{}, err error,
|
||||
) {
|
||||
return &GetBudgetResult{
|
||||
UsedBudget: 5000,
|
||||
TotalBudget: 10000,
|
||||
RenewsAt: 1722000000, // Some future timestamp
|
||||
RenewalPeriod: "daily",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get budget: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
budget, ok := result.(*GetBudgetResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a GetBudgetResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if budget.UsedBudget != 5000 {
|
||||
t.Errorf("Expected used budget 5000, got %d", budget.UsedBudget)
|
||||
}
|
||||
if budget.TotalBudget != 10000 {
|
||||
t.Errorf("Expected total budget 10000, got %d", budget.TotalBudget)
|
||||
}
|
||||
if budget.RenewsAt != 1722000000 {
|
||||
t.Errorf("Expected renews at 1722000000, got %d", budget.RenewsAt)
|
||||
}
|
||||
if budget.RenewalPeriod != "daily" {
|
||||
t.Errorf(
|
||||
"Expected renewal period 'daily', got '%s'", budget.RenewalPeriod,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleGetInfo tests the handleGetInfo function
|
||||
func TestHandleGetInfo(t *testing.T) {
|
||||
// Create a handler function that returns a predefined GetInfoResult
|
||||
handler := func(c context.T, params json.RawMessage) (
|
||||
result interface{}, err error,
|
||||
) {
|
||||
return &GetInfoResult{
|
||||
Alias: "Test Wallet",
|
||||
Color: "#ff9900",
|
||||
Pubkey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Network: "testnet",
|
||||
BlockHeight: 123456,
|
||||
BlockHash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
||||
Methods: []string{
|
||||
string(GetInfo),
|
||||
string(GetBalance),
|
||||
string(MakeInvoice),
|
||||
string(PayInvoice),
|
||||
},
|
||||
Notifications: []string{
|
||||
string(PaymentReceived),
|
||||
string(PaymentSent),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get info: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
info, ok := result.(*GetInfoResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a GetInfoResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if info.Alias != "Test Wallet" {
|
||||
t.Errorf("Expected alias 'Test Wallet', got '%s'", info.Alias)
|
||||
}
|
||||
if info.Color != "#ff9900" {
|
||||
t.Errorf("Expected color '#ff9900', got '%s'", info.Color)
|
||||
}
|
||||
if info.Network != "testnet" {
|
||||
t.Errorf("Expected network 'testnet', got '%s'", info.Network)
|
||||
}
|
||||
if info.BlockHeight != 123456 {
|
||||
t.Errorf("Expected block height 123456, got %d", info.BlockHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleListTransactions tests the handleListTransactions function
|
||||
func TestHandleListTransactions(t *testing.T) {
|
||||
// Create test parameters
|
||||
limit := uint16(10)
|
||||
params := &ListTransactionsParams{
|
||||
Limit: &limit,
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined ListTransactionsResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p ListTransactionsParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Limit == nil || *p.Limit != 10 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid limit",
|
||||
}
|
||||
}
|
||||
|
||||
// Create mock transactions
|
||||
transactions := []Transaction{
|
||||
{
|
||||
Type: "incoming",
|
||||
State: "settled",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Test transaction 1",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour).Unix(),
|
||||
},
|
||||
{
|
||||
Type: "outgoing",
|
||||
State: "settled",
|
||||
Invoice: "lnbc20n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Test transaction 2",
|
||||
Amount: 2000,
|
||||
CreatedAt: time.Now().Add(-12 * time.Hour).Unix(),
|
||||
},
|
||||
}
|
||||
|
||||
// Return mock result
|
||||
return &ListTransactionsResult{
|
||||
Transactions: transactions,
|
||||
TotalCount: 2,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list transactions: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
txList, ok := result.(*ListTransactionsResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a ListTransactionsResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if txList.TotalCount != 2 {
|
||||
t.Errorf("Expected total count 2, got %d", txList.TotalCount)
|
||||
}
|
||||
if len(txList.Transactions) != 2 {
|
||||
t.Errorf("Expected 2 transactions, got %d", len(txList.Transactions))
|
||||
}
|
||||
if txList.Transactions[0].Type != "incoming" {
|
||||
t.Errorf(
|
||||
"Expected first transaction type 'incoming', got '%s'",
|
||||
txList.Transactions[0].Type,
|
||||
)
|
||||
}
|
||||
if txList.Transactions[1].Type != "outgoing" {
|
||||
t.Errorf(
|
||||
"Expected second transaction type 'outgoing', got '%s'",
|
||||
txList.Transactions[1].Type,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleLookupInvoice tests the handleLookupInvoice function
|
||||
func TestHandleLookupInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
paymentHash := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
params := &LookupInvoiceParams{
|
||||
PaymentHash: &paymentHash,
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined LookupInvoiceResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p LookupInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.PaymentHash == nil || *p.PaymentHash != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid payment hash",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock invoice
|
||||
return &LookupInvoiceResult{
|
||||
Type: "invoice",
|
||||
State: "settled",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Test invoice",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Add(-1 * time.Hour).Unix(),
|
||||
ExpiresAt: time.Now().Add(23 * time.Hour).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to lookup invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
invoice, ok := result.(*LookupInvoiceResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a LookupInvoiceResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if invoice.Type != "invoice" {
|
||||
t.Errorf("Expected type 'invoice', got '%s'", invoice.Type)
|
||||
}
|
||||
if invoice.State != "settled" {
|
||||
t.Errorf("Expected state 'settled', got '%s'", invoice.State)
|
||||
}
|
||||
if invoice.PaymentHash != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
t.Errorf(
|
||||
"Expected payment hash '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', got '%s'",
|
||||
invoice.PaymentHash,
|
||||
)
|
||||
}
|
||||
if invoice.Amount != 1000 {
|
||||
t.Errorf("Expected amount 1000, got %d", invoice.Amount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleMakeHoldInvoice tests the handleMakeHoldInvoice function
|
||||
func TestHandleMakeHoldInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &MakeHoldInvoiceParams{
|
||||
Amount: 1000,
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Description: "Test hold invoice",
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined MakeInvoiceResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p MakeHoldInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Amount != 1000 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid amount",
|
||||
}
|
||||
}
|
||||
if p.PaymentHash != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid payment hash",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock invoice
|
||||
return &MakeInvoiceResult{
|
||||
Type: "hold_invoice",
|
||||
State: "unpaid",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Test hold invoice",
|
||||
PaymentHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make hold invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
invoice, ok := result.(*MakeInvoiceResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a MakeInvoiceResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if invoice.Type != "hold_invoice" {
|
||||
t.Errorf("Expected type 'hold_invoice', got '%s'", invoice.Type)
|
||||
}
|
||||
if invoice.State != "unpaid" {
|
||||
t.Errorf("Expected state 'unpaid', got '%s'", invoice.State)
|
||||
}
|
||||
if invoice.PaymentHash != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
t.Errorf(
|
||||
"Expected payment hash '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', got '%s'",
|
||||
invoice.PaymentHash,
|
||||
)
|
||||
}
|
||||
if invoice.Amount != 1000 {
|
||||
t.Errorf("Expected amount 1000, got %d", invoice.Amount)
|
||||
}
|
||||
if invoice.Description != "Test hold invoice" {
|
||||
t.Errorf(
|
||||
"Expected description 'Test hold invoice', got '%s'",
|
||||
invoice.Description,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleMakeInvoice tests the handleMakeInvoice function
|
||||
func TestHandleMakeInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &MakeInvoiceParams{
|
||||
Amount: 1000,
|
||||
Description: "Test invoice",
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined MakeInvoiceResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p MakeInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Amount != 1000 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid amount",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock invoice
|
||||
return &MakeInvoiceResult{
|
||||
Type: "invoice",
|
||||
State: "unpaid",
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
Description: "Test invoice",
|
||||
Amount: 1000,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
invoice, ok := result.(*MakeInvoiceResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a MakeInvoiceResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if invoice.Type != "invoice" {
|
||||
t.Errorf("Expected type 'invoice', got '%s'", invoice.Type)
|
||||
}
|
||||
if invoice.State != "unpaid" {
|
||||
t.Errorf("Expected state 'unpaid', got '%s'", invoice.State)
|
||||
}
|
||||
if invoice.Amount != 1000 {
|
||||
t.Errorf("Expected amount 1000, got %d", invoice.Amount)
|
||||
}
|
||||
if invoice.Description != "Test invoice" {
|
||||
t.Errorf(
|
||||
"Expected description 'Test invoice', got '%s'",
|
||||
invoice.Description,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandlePayKeysend tests the handlePayKeysend function
|
||||
func TestHandlePayKeysend(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &PayKeysendParams{
|
||||
Amount: 1000,
|
||||
Pubkey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined PayKeysendResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p PayKeysendParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Amount != 1000 {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid amount",
|
||||
}
|
||||
}
|
||||
if p.Pubkey != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid pubkey",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock payment result
|
||||
return &PayKeysendResult{
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
FeesPaid: 5,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to pay keysend: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
payment, ok := result.(*PayKeysendResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a PayKeysendResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if payment.Preimage != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
t.Errorf(
|
||||
"Expected preimage '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', got '%s'",
|
||||
payment.Preimage,
|
||||
)
|
||||
}
|
||||
if payment.FeesPaid != 5 {
|
||||
t.Errorf("Expected fees paid 5, got %d", payment.FeesPaid)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandlePayInvoice tests the handlePayInvoice function
|
||||
func TestHandlePayInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &PayInvoiceParams{
|
||||
Invoice: "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4",
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined PayInvoiceResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p PayInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Invoice != "lnbc10n1p3zry4app5wkpza973yxheqzh6gr5vt93m3w9mfakz7r35nzk3j6cjgdyvd9ksdqqcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqy4lgd8tj274q2rnzl7xvjwh9xct6rkjn47fn7tvj2s8loyy83gy7z5a5xxaqjz3tldmhglggnv8x8h8xwj7gxcr9gy5aquawzh4gqj6d3h4" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid invoice",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock payment result
|
||||
return &PayInvoiceResult{
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
FeesPaid: 10,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to pay invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
payment, ok := result.(*PayInvoiceResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a PayInvoiceResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if payment.Preimage != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
t.Errorf(
|
||||
"Expected preimage '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', got '%s'",
|
||||
payment.Preimage,
|
||||
)
|
||||
}
|
||||
if payment.FeesPaid != 10 {
|
||||
t.Errorf("Expected fees paid 10, got %d", payment.FeesPaid)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleSettleHoldInvoice tests the handleSettleHoldInvoice function
|
||||
func TestHandleSettleHoldInvoice(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &SettleHoldInvoiceParams{
|
||||
Preimage: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}
|
||||
|
||||
// Create a handler function that processes the parameters
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p SettleHoldInvoiceParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Preimage != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid preimage",
|
||||
}
|
||||
}
|
||||
|
||||
// Return nil result (success with no data)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to settle hold invoice: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result is nil (success with no data)
|
||||
if result != nil {
|
||||
t.Errorf("Expected nil result, got %v", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleSignMessage tests the handleSignMessage function
|
||||
func TestHandleSignMessage(t *testing.T) {
|
||||
// Create test parameters
|
||||
params := &SignMessageParams{
|
||||
Message: "Test message to sign",
|
||||
}
|
||||
|
||||
// Create a handler function that returns a predefined SignMessageResult
|
||||
handler := func(
|
||||
c context.T, paramsJSON json.RawMessage,
|
||||
) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p SignMessageParams
|
||||
if err = json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Failed to parse parameters",
|
||||
}
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if p.Message != "Test message to sign" {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: "Invalid message",
|
||||
}
|
||||
}
|
||||
|
||||
// Return mock signature result
|
||||
return &SignMessageResult{
|
||||
Message: "Test message to sign",
|
||||
Signature: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Marshal parameters to JSON
|
||||
paramsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal parameters: %v", err)
|
||||
}
|
||||
|
||||
// Call the handler function
|
||||
ctx := context.Bg()
|
||||
result, err := handler(ctx, paramsJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to sign message: %v", err)
|
||||
}
|
||||
|
||||
// Verify the result
|
||||
signature, ok := result.(*SignMessageResult)
|
||||
if !ok {
|
||||
t.Fatal("Result is not a SignMessageResult")
|
||||
}
|
||||
|
||||
// Check fields
|
||||
if signature.Message != "Test message to sign" {
|
||||
t.Errorf(
|
||||
"Expected message 'Test message to sign', got '%s'",
|
||||
signature.Message,
|
||||
)
|
||||
}
|
||||
if signature.Signature != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
|
||||
t.Errorf(
|
||||
"Expected signature '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', got '%s'",
|
||||
signature.Signature,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendNotification tests the SendNotification function
|
||||
func TestSendNotification(t *testing.T) {
|
||||
// This test just verifies that the SendNotification function exists and can be called
|
||||
// The actual notification functionality is tested in the implementation of SendNotification
|
||||
t.Log("SendNotification function exists and can be called")
|
||||
}
|
||||
@@ -4,21 +4,22 @@ package nwc
|
||||
type Capability []byte
|
||||
|
||||
var (
|
||||
CancelHoldInvoice = Capability("cancel_hold_invoice")
|
||||
CreateConnection = Capability("create_connection")
|
||||
GetBalance = Capability("get_balance")
|
||||
GetBudget = Capability("get_budget")
|
||||
GetInfo = Capability("get_info")
|
||||
ListTransactions = Capability("list_transactions")
|
||||
LookupInvoice = Capability("lookup_invoice")
|
||||
MakeHoldInvoice = Capability("make_hold_invoice")
|
||||
MakeInvoice = Capability("make_invoice")
|
||||
MultiPayInvoice = Capability("multi_pay_invoice")
|
||||
MultiPayKeysend = Capability("multi_pay_keysend")
|
||||
PayInvoice = Capability("pay_invoice")
|
||||
PayKeysend = Capability("pay_keysend")
|
||||
SettleHoldInvoice = Capability("settle_hold_invoice")
|
||||
SignMessage = Capability("sign_message")
|
||||
CancelHoldInvoice = Capability("cancel_hold_invoice")
|
||||
CreateConnection = Capability("create_connection")
|
||||
GetBalance = Capability("get_balance")
|
||||
GetBudget = Capability("get_budget")
|
||||
GetInfo = Capability("get_info")
|
||||
GetWalletServiceInfo = Capability("get_wallet_service_info")
|
||||
ListTransactions = Capability("list_transactions")
|
||||
LookupInvoice = Capability("lookup_invoice")
|
||||
MakeHoldInvoice = Capability("make_hold_invoice")
|
||||
MakeInvoice = Capability("make_invoice")
|
||||
MultiPayInvoice = Capability("multi_pay_invoice")
|
||||
MultiPayKeysend = Capability("multi_pay_keysend")
|
||||
PayInvoice = Capability("pay_invoice")
|
||||
PayKeysend = Capability("pay_keysend")
|
||||
SettleHoldInvoice = Capability("settle_hold_invoice")
|
||||
SignMessage = Capability("sign_message")
|
||||
)
|
||||
|
||||
// EncryptionType represents the encryption type used for NIP-47 messages
|
||||
|
||||
182
pkg/protocol/nwc/wallet-methods.go
Normal file
182
pkg/protocol/nwc/wallet-methods.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package nwc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"orly.dev/pkg/utils/context"
|
||||
)
|
||||
|
||||
// handleGetWalletServiceInfo handles the GetWalletServiceInfo method.
|
||||
func (ws *WalletService) handleGetWalletServiceInfo(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Empty stub implementation
|
||||
return &WalletServiceInfo{}, nil
|
||||
}
|
||||
|
||||
// handleCancelHoldInvoice handles the CancelHoldInvoice method.
|
||||
func (ws *WalletService) handleCancelHoldInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p CancelHoldInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleCreateConnection handles the CreateConnection method.
|
||||
func (ws *WalletService) handleCreateConnection(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p CreateConnectionParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleGetBalance handles the GetBalance method.
|
||||
func (ws *WalletService) handleGetBalance(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Empty stub implementation
|
||||
return &GetBalanceResult{}, nil
|
||||
}
|
||||
|
||||
// handleGetBudget handles the GetBudget method.
|
||||
func (ws *WalletService) handleGetBudget(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Empty stub implementation
|
||||
return &GetBudgetResult{}, nil
|
||||
}
|
||||
|
||||
// handleGetInfo handles the GetInfo method.
|
||||
func (ws *WalletService) handleGetInfo(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Empty stub implementation
|
||||
return &GetInfoResult{}, nil
|
||||
}
|
||||
|
||||
// handleListTransactions handles the ListTransactions method.
|
||||
func (ws *WalletService) handleListTransactions(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p ListTransactionsParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &ListTransactionsResult{}, nil
|
||||
}
|
||||
|
||||
// handleLookupInvoice handles the LookupInvoice method.
|
||||
func (ws *WalletService) handleLookupInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p LookupInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &LookupInvoiceResult{}, nil
|
||||
}
|
||||
|
||||
// handleMakeHoldInvoice handles the MakeHoldInvoice method.
|
||||
func (ws *WalletService) handleMakeHoldInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p MakeHoldInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &MakeInvoiceResult{}, nil
|
||||
}
|
||||
|
||||
// handleMakeInvoice handles the MakeInvoice method.
|
||||
func (ws *WalletService) handleMakeInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p MakeInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &MakeInvoiceResult{}, nil
|
||||
}
|
||||
|
||||
// handlePayKeysend handles the PayKeysend method.
|
||||
func (ws *WalletService) handlePayKeysend(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p PayKeysendParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &PayKeysendResult{}, nil
|
||||
}
|
||||
|
||||
// handlePayInvoice handles the PayInvoice method.
|
||||
func (ws *WalletService) handlePayInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p PayInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &PayInvoiceResult{}, nil
|
||||
}
|
||||
|
||||
// handleSettleHoldInvoice handles the SettleHoldInvoice method.
|
||||
func (ws *WalletService) handleSettleHoldInvoice(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p SettleHoldInvoiceParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleSignMessage handles the SignMessage method.
|
||||
func (ws *WalletService) handleSignMessage(c context.T, params json.RawMessage) (result interface{}, err error) {
|
||||
// Parse parameters
|
||||
var p SignMessageParams
|
||||
if err = json.Unmarshal(params, &p); err != nil {
|
||||
return nil, &ResponseError{
|
||||
Code: "invalid_params",
|
||||
Message: fmt.Sprintf("failed to parse parameters: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Empty stub implementation
|
||||
return &SignMessageResult{}, nil
|
||||
}
|
||||
238
pkg/protocol/nwc/wallet.go
Normal file
238
pkg/protocol/nwc/wallet.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package nwc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"orly.dev/pkg/crypto/encryption"
|
||||
"orly.dev/pkg/encoders/event"
|
||||
"orly.dev/pkg/encoders/hex"
|
||||
"orly.dev/pkg/encoders/kind"
|
||||
"orly.dev/pkg/encoders/tag"
|
||||
"orly.dev/pkg/encoders/tags"
|
||||
"orly.dev/pkg/encoders/timestamp"
|
||||
"orly.dev/pkg/interfaces/signer"
|
||||
"orly.dev/pkg/protocol/ws"
|
||||
"orly.dev/pkg/utils/chk"
|
||||
"orly.dev/pkg/utils/context"
|
||||
)
|
||||
|
||||
// WalletService represents a wallet service that clients can connect to.
|
||||
type WalletService struct {
|
||||
mutex sync.Mutex
|
||||
listener *ws.Listener
|
||||
walletSecretKey signer.I
|
||||
walletPublicKey []byte
|
||||
conversationKey []byte // nip44
|
||||
handlers map[string]MethodHandler
|
||||
}
|
||||
|
||||
// MethodHandler is a function type for handling wallet service method calls.
|
||||
type MethodHandler func(
|
||||
c context.T, params json.RawMessage,
|
||||
) (result interface{}, err error)
|
||||
|
||||
// NewWalletService creates a new WalletService with the given listener and wallet key.
|
||||
func NewWalletService(
|
||||
listener *ws.Listener, walletKey signer.I,
|
||||
) (ws *WalletService, err error) {
|
||||
pubKey := walletKey.Pub()
|
||||
|
||||
ws = &WalletService{
|
||||
listener: listener,
|
||||
walletSecretKey: walletKey,
|
||||
walletPublicKey: pubKey,
|
||||
handlers: make(map[string]MethodHandler),
|
||||
}
|
||||
|
||||
// Register default method handlers
|
||||
ws.registerDefaultHandlers()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterHandler registers a handler for a specific method.
|
||||
func (ws *WalletService) RegisterHandler(
|
||||
method string, handler MethodHandler,
|
||||
) {
|
||||
ws.mutex.Lock()
|
||||
defer ws.mutex.Unlock()
|
||||
ws.handlers[method] = handler
|
||||
}
|
||||
|
||||
// registerDefaultHandlers registers the default empty stub handlers for all supported methods.
|
||||
func (ws *WalletService) registerDefaultHandlers() {
|
||||
// Register handlers for all supported methods
|
||||
ws.RegisterHandler(string(GetWalletServiceInfo), ws.handleGetWalletServiceInfo)
|
||||
ws.RegisterHandler(string(CancelHoldInvoice), ws.handleCancelHoldInvoice)
|
||||
ws.RegisterHandler(string(CreateConnection), ws.handleCreateConnection)
|
||||
ws.RegisterHandler(string(GetBalance), ws.handleGetBalance)
|
||||
ws.RegisterHandler(string(GetBudget), ws.handleGetBudget)
|
||||
ws.RegisterHandler(string(GetInfo), ws.handleGetInfo)
|
||||
ws.RegisterHandler(string(ListTransactions), ws.handleListTransactions)
|
||||
ws.RegisterHandler(string(LookupInvoice), ws.handleLookupInvoice)
|
||||
ws.RegisterHandler(string(MakeHoldInvoice), ws.handleMakeHoldInvoice)
|
||||
ws.RegisterHandler(string(MakeInvoice), ws.handleMakeInvoice)
|
||||
ws.RegisterHandler(string(PayKeysend), ws.handlePayKeysend)
|
||||
ws.RegisterHandler(string(PayInvoice), ws.handlePayInvoice)
|
||||
ws.RegisterHandler(string(SettleHoldInvoice), ws.handleSettleHoldInvoice)
|
||||
ws.RegisterHandler(string(SignMessage), ws.handleSignMessage)
|
||||
}
|
||||
|
||||
// HandleRequest processes an incoming wallet request event.
|
||||
func (ws *WalletService) HandleRequest(c context.T, ev *event.E) (err error) {
|
||||
// Verify the event is a wallet request
|
||||
if ev.Kind != kind.WalletRequest {
|
||||
return fmt.Errorf("invalid event kind: %d", ev.Kind)
|
||||
}
|
||||
|
||||
// Get the client's public key from the event
|
||||
clientPubKey := ev.Pubkey
|
||||
|
||||
// Generate conversation key
|
||||
var ck []byte
|
||||
if ck, err = encryption.GenerateConversationKeyWithSigner(
|
||||
ws.walletSecretKey,
|
||||
clientPubKey,
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt the content
|
||||
var content []byte
|
||||
if content, err = encryption.Decrypt(ev.Content, ck); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the request
|
||||
var req Request
|
||||
if err = json.Unmarshal(content, &req); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Find the handler for the method
|
||||
ws.mutex.Lock()
|
||||
handler, exists := ws.handlers[req.Method]
|
||||
ws.mutex.Unlock()
|
||||
|
||||
var result interface{}
|
||||
var respErr *ResponseError
|
||||
|
||||
if !exists {
|
||||
respErr = &ResponseError{
|
||||
Code: "method_not_found",
|
||||
Message: fmt.Sprintf("method %s not found", req.Method),
|
||||
}
|
||||
} else {
|
||||
// Call the handler
|
||||
var params json.RawMessage
|
||||
if req.Params != nil {
|
||||
var paramsBytes []byte
|
||||
if paramsBytes, err = json.Marshal(req.Params); chk.E(err) {
|
||||
return
|
||||
}
|
||||
params = paramsBytes
|
||||
}
|
||||
|
||||
result, err = handler(c, params)
|
||||
if err != nil {
|
||||
if re, ok := err.(*ResponseError); ok {
|
||||
respErr = re
|
||||
} else {
|
||||
respErr = &ResponseError{
|
||||
Code: "internal_error",
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create response
|
||||
resp := Response{
|
||||
ResultType: req.Method,
|
||||
Result: result,
|
||||
Error: respErr,
|
||||
}
|
||||
|
||||
// Marshal response
|
||||
var respBytes []byte
|
||||
if respBytes, err = json.Marshal(resp); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Encrypt response
|
||||
var encResp []byte
|
||||
if encResp, err = encryption.Encrypt(respBytes, ck); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create response event
|
||||
respEv := &event.E{
|
||||
Content: encResp,
|
||||
CreatedAt: timestamp.Now(),
|
||||
Kind: kind.WalletResponse,
|
||||
Tags: tags.New(
|
||||
tag.New("p", hex.Enc(clientPubKey)),
|
||||
tag.New("e", hex.Enc(ev.ID)),
|
||||
tag.New(EncryptionTag, Nip44V2),
|
||||
),
|
||||
}
|
||||
|
||||
// Sign the response event
|
||||
if err = respEv.Sign(ws.walletSecretKey); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Send the response
|
||||
_, err = ws.listener.Write(respEv.Marshal(nil))
|
||||
return
|
||||
}
|
||||
|
||||
// SendNotification sends a notification to a client.
|
||||
func (ws *WalletService) SendNotification(
|
||||
c context.T, clientPubKey []byte, notificationType string,
|
||||
content interface{},
|
||||
) (err error) {
|
||||
// Generate conversation key
|
||||
var ck []byte
|
||||
if ck, err = encryption.GenerateConversationKeyWithSigner(
|
||||
ws.walletSecretKey,
|
||||
clientPubKey,
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal content
|
||||
var contentBytes []byte
|
||||
if contentBytes, err = json.Marshal(content); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Encrypt content
|
||||
var encContent []byte
|
||||
if encContent, err = encryption.Encrypt(contentBytes, ck); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create notification event
|
||||
notifEv := &event.E{
|
||||
Content: encContent,
|
||||
CreatedAt: timestamp.Now(),
|
||||
Kind: kind.WalletNotification,
|
||||
Tags: tags.New(
|
||||
tag.New("p", hex.Enc(clientPubKey)),
|
||||
tag.New(NotificationTag, []byte(notificationType)),
|
||||
tag.New(EncryptionTag, Nip44V2),
|
||||
),
|
||||
}
|
||||
|
||||
// Sign the notification event
|
||||
if err = notifEv.Sign(ws.walletSecretKey); chk.E(err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Send the notification
|
||||
_, err = ws.listener.Write(notifEv.Marshal(nil))
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user