nip05: use typed pubkeys.
This commit is contained in:
@@ -14,9 +14,9 @@ import (
|
||||
var NIP05_REGEX = regexp.MustCompile(`^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$`)
|
||||
|
||||
type WellKnownResponse struct {
|
||||
Names map[string]string `json:"names"`
|
||||
Relays map[string][]string `json:"relays,omitempty"`
|
||||
NIP46 map[string][]string `json:"nip46,omitempty"`
|
||||
Names map[string]nostr.PubKey `json:"names"`
|
||||
Relays map[nostr.PubKey][]string `json:"relays,omitempty"`
|
||||
NIP46 map[nostr.PubKey][]string `json:"nip46,omitempty"`
|
||||
}
|
||||
|
||||
func IsValidIdentifier(input string) bool {
|
||||
@@ -40,17 +40,12 @@ func QueryIdentifier(ctx context.Context, fullname string) (*nostr.ProfilePointe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubkeyh, ok := result.Names[name]
|
||||
pubkey, ok := result.Names[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no entry for name '%s'", name)
|
||||
}
|
||||
|
||||
pubkey, err := nostr.PubKeyFromHex(pubkeyh)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got an invalid public key '%s'", pubkeyh)
|
||||
}
|
||||
|
||||
relays, _ := result.Relays[pubkeyh]
|
||||
relays, _ := result.Relays[pubkey]
|
||||
return &nostr.ProfilePointer{
|
||||
PublicKey: pubkey,
|
||||
Relays: relays,
|
||||
|
||||
287
nip05/nip05_easyjson.go
Normal file
287
nip05/nip05_easyjson.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package nip05
|
||||
|
||||
import (
|
||||
json "encoding/json"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
nostr "fiatjaf.com/nostr"
|
||||
easyjson "github.com/mailru/easyjson"
|
||||
jlexer "github.com/mailru/easyjson/jlexer"
|
||||
jwriter "github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// suppress unused package warning
|
||||
var (
|
||||
_ *json.RawMessage
|
||||
_ *jlexer.Lexer
|
||||
_ *jwriter.Writer
|
||||
_ easyjson.Marshaler
|
||||
)
|
||||
|
||||
func easyjsonDecode(in *jlexer.Lexer, out *WellKnownResponse) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "names":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
} else {
|
||||
in.Delim('{')
|
||||
out.Names = make(map[string]nostr.PubKey)
|
||||
for !in.IsDelim('}') {
|
||||
key := string(in.String())
|
||||
in.WantColon()
|
||||
var pk nostr.PubKey
|
||||
if data := in.Raw(); in.Ok() {
|
||||
var err error
|
||||
if len(data) < 2 {
|
||||
err = errors.New("names[]{pubkey} must be a string")
|
||||
} else {
|
||||
data = data[1 : len(data)-1]
|
||||
pk, err = nostr.PubKeyFromHex(unsafe.String(unsafe.SliceData(data), len(data)))
|
||||
}
|
||||
in.AddError(err)
|
||||
}
|
||||
out.Names[key] = pk
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
}
|
||||
case "relays":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
} else {
|
||||
in.Delim('{')
|
||||
if !in.IsDelim('}') {
|
||||
out.Relays = make(map[nostr.PubKey][]string)
|
||||
} else {
|
||||
out.Relays = nil
|
||||
}
|
||||
for !in.IsDelim('}') {
|
||||
var key nostr.PubKey
|
||||
if data := in.Raw(); in.Ok() {
|
||||
var err error
|
||||
if len(data) < 2 {
|
||||
err = errors.New("relays[pubkey] must be a string")
|
||||
} else {
|
||||
data = data[1 : len(data)-1]
|
||||
key, err = nostr.PubKeyFromHex(unsafe.String(unsafe.SliceData(data), len(data)))
|
||||
}
|
||||
in.AddError(err)
|
||||
}
|
||||
in.WantColon()
|
||||
var relays []string
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
relays = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if relays == nil {
|
||||
if !in.IsDelim(']') {
|
||||
relays = make([]string, 0, 4)
|
||||
} else {
|
||||
relays = []string{}
|
||||
}
|
||||
} else {
|
||||
relays = (relays)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
relays = append(relays, string(in.String()))
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
out.Relays[key] = relays
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
}
|
||||
case "nip46":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
} else {
|
||||
in.Delim('{')
|
||||
if !in.IsDelim('}') {
|
||||
out.NIP46 = make(map[nostr.PubKey][]string)
|
||||
} else {
|
||||
out.NIP46 = nil
|
||||
}
|
||||
for !in.IsDelim('}') {
|
||||
var key nostr.PubKey
|
||||
if data := in.Raw(); in.Ok() {
|
||||
var err error
|
||||
if len(data) < 2 {
|
||||
err = errors.New("nip46[pubkey] must be a string")
|
||||
} else {
|
||||
data = data[1 : len(data)-1]
|
||||
key, err = nostr.PubKeyFromHex(unsafe.String(unsafe.SliceData(data), len(data)))
|
||||
}
|
||||
in.AddError(err)
|
||||
}
|
||||
in.WantColon()
|
||||
var bunkers []string
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
bunkers = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if bunkers == nil {
|
||||
if !in.IsDelim(']') {
|
||||
bunkers = make([]string, 0, 4)
|
||||
} else {
|
||||
bunkers = []string{}
|
||||
}
|
||||
} else {
|
||||
bunkers = (bunkers)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var bunker string
|
||||
bunker = string(in.String())
|
||||
bunkers = append(bunkers, bunker)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
out.NIP46[key] = bunkers
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
}
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
||||
func easyjsonEncode(out *jwriter.Writer, in WellKnownResponse) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"names\":"
|
||||
out.RawString(prefix[1:])
|
||||
if in.Names == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 {
|
||||
out.RawString(`null`)
|
||||
} else {
|
||||
out.RawByte('{')
|
||||
isFirst := true
|
||||
for name, pk := range in.Names {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(name)
|
||||
out.RawByte(':')
|
||||
out.String(pk.Hex())
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
}
|
||||
if len(in.Relays) != 0 {
|
||||
const prefix string = ",\"relays\":"
|
||||
out.RawString(prefix)
|
||||
{
|
||||
out.RawByte('{')
|
||||
isFirst := true
|
||||
for pk, relays := range in.Relays {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(pk.Hex())
|
||||
out.RawByte(':')
|
||||
if relays == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for i, relay := range relays {
|
||||
if i > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(relay)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
}
|
||||
if len(in.NIP46) != 0 {
|
||||
const prefix string = ",\"nip46\":"
|
||||
out.RawString(prefix)
|
||||
{
|
||||
out.RawByte('{')
|
||||
isFirst := true
|
||||
for pk, bunkers := range in.NIP46 {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
} else {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(pk.Hex())
|
||||
out.RawByte(':')
|
||||
if bunkers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
out.RawByte('[')
|
||||
for i, bunker := range bunkers {
|
||||
if i > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(bunker)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v WellKnownResponse) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjsonEncode(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v WellKnownResponse) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjsonEncode(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *WellKnownResponse) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjsonDecode(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *WellKnownResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjsonDecode(l, v)
|
||||
}
|
||||
@@ -2,10 +2,12 @@ package nip05
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"fiatjaf.com/nostr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
@@ -38,11 +40,11 @@ func TestParse(t *testing.T) {
|
||||
func TestQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expectedKey string
|
||||
expectedKey nostr.PubKey
|
||||
expectError bool
|
||||
}{
|
||||
{"fiatjaf.com", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", false},
|
||||
{"htlc@fiatjaf.com", "f9dd6a762506260b38a2d3e5b464213c2e47fa3877429fe9ee60e071a31a07d7", false},
|
||||
{"fiatjaf.com", nostr.MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), false},
|
||||
{"htlc@fiatjaf.com", nostr.MustPubKeyFromHex("f9dd6a762506260b38a2d3e5b464213c2e47fa3877429fe9ee60e071a31a07d7"), false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -51,7 +53,32 @@ func TestQuery(t *testing.T) {
|
||||
assert.Error(t, err, "expected error for input: %s", test.input)
|
||||
} else {
|
||||
assert.NoError(t, err, "did not expect error for input: %s", test.input)
|
||||
assert.Equal(t, nostr.MustPubKeyFromHex(test.expectedKey), pp.PublicKey, "for input: %s", test.input)
|
||||
assert.Equal(t, test.expectedKey, pp.PublicKey, "for input: %s", test.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponse(t *testing.T) {
|
||||
pk1 := nostr.Generate().Public()
|
||||
pk2 := nostr.Generate().Public()
|
||||
|
||||
resp := WellKnownResponse{
|
||||
Names: map[string]nostr.PubKey{
|
||||
"foo": pk1,
|
||||
"bar": pk2,
|
||||
},
|
||||
Relays: map[nostr.PubKey][]string{
|
||||
pk1: {"wss://a.com"},
|
||||
pk2: {"wss://a.com", "wss://b.com"},
|
||||
},
|
||||
}
|
||||
|
||||
respj, err := json.Marshal(resp)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `{"names":{"foo":"`+pk1.Hex()+`","bar":"`+pk2.Hex()+`"},"relays":{"`+pk1.Hex()+`":["wss://a.com"],"`+pk2.Hex()+`":["wss://a.com","wss://b.com"]}}`, string(respj))
|
||||
|
||||
back := WellKnownResponse{}
|
||||
err = json.Unmarshal(respj, &back)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, resp, back)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user