mirror of
https://github.com/fiatjaf/nak.git
synced 2024-10-30 09:09:08 -04:00
implement nip86 client: making management RPC calls to relays.
This commit is contained in:
parent
316d94166e
commit
5ee0036128
2
go.mod
2
go.mod
|
@ -10,7 +10,7 @@ require (
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/mailru/easyjson v0.7.7
|
github.com/mailru/easyjson v0.7.7
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0
|
github.com/nbd-wtf/go-nostr v0.34.2
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5
|
github.com/nbd-wtf/nostr-sdk v0.0.5
|
||||||
github.com/urfave/cli/v3 v3.0.0-alpha9
|
github.com/urfave/cli/v3 v3.0.0-alpha9
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -79,8 +79,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0 h1:E7tDHFx42gvWwFv1Eysn+NxJqGLmo21x/VEwj2+F21E=
|
github.com/nbd-wtf/go-nostr v0.34.2 h1:9b4qZ29DhQf9xEWN8/7zfDD868r1jFbpjrR3c+BHc+E=
|
||||||
github.com/nbd-wtf/go-nostr v0.34.0/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs=
|
github.com/nbd-wtf/go-nostr v0.34.2/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs=
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5 h1:rec+FcDizDVO0W25PX0lgYMXvP7zNNOgI3Fu9UCm4BY=
|
github.com/nbd-wtf/nostr-sdk v0.0.5 h1:rec+FcDizDVO0W25PX0lgYMXvP7zNNOgI3Fu9UCm4BY=
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.5/go.mod h1:iJJsikesCGLNFZ9dLqhLPDzdt924EagUmdQxT3w2Lmk=
|
github.com/nbd-wtf/nostr-sdk v0.0.5/go.mod h1:iJJsikesCGLNFZ9dLqhLPDzdt924EagUmdQxT3w2Lmk=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
|
170
relay.go
170
relay.go
|
@ -1,11 +1,20 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/nbd-wtf/go-nostr"
|
||||||
"github.com/nbd-wtf/go-nostr/nip11"
|
"github.com/nbd-wtf/go-nostr/nip11"
|
||||||
|
"github.com/nbd-wtf/go-nostr/nip86"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,4 +41,165 @@ var relay = &cli.Command{
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
Commands: (func() []*cli.Command {
|
||||||
|
commands := make([]*cli.Command, 0, 12)
|
||||||
|
|
||||||
|
for _, def := range []struct {
|
||||||
|
method string
|
||||||
|
args []string
|
||||||
|
}{{"allowpubkey", []string{"pubkey", "reason"}}} {
|
||||||
|
flags := make([]cli.Flag, len(def.args), len(def.args)+4)
|
||||||
|
for i, argName := range def.args {
|
||||||
|
flags[i] = declareFlag(argName)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = append(flags,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "sec",
|
||||||
|
Usage: "secret key to sign the event, as nsec, ncryptsec or hex",
|
||||||
|
DefaultText: "the key '1'",
|
||||||
|
Value: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "prompt-sec",
|
||||||
|
Usage: "prompt the user to paste a hex or nsec with which to sign the event",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "connect",
|
||||||
|
Usage: "sign event using NIP-46, expects a bunker://... URL",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "connect-as",
|
||||||
|
Usage: "private key to when communicating with the bunker given on --connect",
|
||||||
|
DefaultText: "a random key",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := &cli.Command{
|
||||||
|
Name: def.method,
|
||||||
|
Usage: fmt.Sprintf(`the "%s" relay management RPC call`, def.method),
|
||||||
|
Description: fmt.Sprintf(
|
||||||
|
`the "%s" management RPC call, see https://nips.nostr.com/86 for more information`, def.method),
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
params := make([]any, len(def.args))
|
||||||
|
for i, argName := range def.args {
|
||||||
|
params[i] = getArgument(c, argName)
|
||||||
|
}
|
||||||
|
req := nip86.Request{Method: def.method, Params: params}
|
||||||
|
reqj, _ := json.Marshal(req)
|
||||||
|
|
||||||
|
relayUrls := c.Args().Slice()
|
||||||
|
if len(relayUrls) == 0 {
|
||||||
|
stdout(string(reqj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sec, bunker, err := gatherSecretKeyOrBunkerFromArguments(ctx, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, relayUrl := range relayUrls {
|
||||||
|
httpUrl := "http" + nostr.NormalizeURL(relayUrl)[2:]
|
||||||
|
log("calling %s... ", httpUrl)
|
||||||
|
body := bytes.NewBuffer(nil)
|
||||||
|
body.Write(reqj)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", httpUrl, body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization
|
||||||
|
hostname := strings.Split(strings.Split(httpUrl, "://")[1], "/")[0]
|
||||||
|
payloadHash := sha256.Sum256(reqj)
|
||||||
|
authEvent := nostr.Event{
|
||||||
|
Kind: 27235,
|
||||||
|
CreatedAt: nostr.Now(),
|
||||||
|
Tags: nostr.Tags{
|
||||||
|
{"host", hostname},
|
||||||
|
{"payload", hex.EncodeToString(payloadHash[:])},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if bunker != nil {
|
||||||
|
if err := bunker.SignEvent(ctx, &authEvent); err != nil {
|
||||||
|
return fmt.Errorf("failed to sign with bunker: %w", err)
|
||||||
|
}
|
||||||
|
} else if err := authEvent.Sign(sec); err != nil {
|
||||||
|
return fmt.Errorf("error signing with provided key: %w", err)
|
||||||
|
}
|
||||||
|
evtj, _ := json.Marshal(authEvent)
|
||||||
|
req.Header.Set("Authorization", "Nostr "+base64.StdEncoding.EncodeToString(evtj))
|
||||||
|
|
||||||
|
// Content-Type
|
||||||
|
req.Header.Set("Content-Type", "application/nostr+json+rpc")
|
||||||
|
|
||||||
|
// make request to relay
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log("failed: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log("failed to read response: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
log("failed with status %d\n", resp.StatusCode)
|
||||||
|
bodyPrintable := string(b)
|
||||||
|
if len(bodyPrintable) > 300 {
|
||||||
|
bodyPrintable = bodyPrintable[0:297] + "..."
|
||||||
|
}
|
||||||
|
log(bodyPrintable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var response nip86.Response
|
||||||
|
if err := json.Unmarshal(b, &response); err != nil {
|
||||||
|
log("bad json response: %s\n", err)
|
||||||
|
bodyPrintable := string(b)
|
||||||
|
if len(bodyPrintable) > 300 {
|
||||||
|
bodyPrintable = bodyPrintable[0:297] + "..."
|
||||||
|
}
|
||||||
|
log(bodyPrintable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
// print the result
|
||||||
|
log("\n")
|
||||||
|
pretty, _ := json.MarshalIndent(response, "", " ")
|
||||||
|
stdout(string(pretty))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
commands = append(commands, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
})(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func declareFlag(argName string) cli.Flag {
|
||||||
|
usage := "parameter for this management RPC call, see https://nips.nostr.com/86 for more information."
|
||||||
|
switch argName {
|
||||||
|
case "kind":
|
||||||
|
return &cli.IntFlag{Name: argName, Required: true, Usage: usage}
|
||||||
|
case "reason":
|
||||||
|
return &cli.StringFlag{Name: argName, Usage: usage}
|
||||||
|
default:
|
||||||
|
return &cli.StringFlag{Name: argName, Required: true, Usage: usage}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArgument(c *cli.Command, argName string) any {
|
||||||
|
switch argName {
|
||||||
|
case "kind":
|
||||||
|
return c.Int(argName)
|
||||||
|
default:
|
||||||
|
return c.String(argName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user