nip05: use typed pubkeys.
This commit is contained in:
@@ -14,9 +14,9 @@ import (
|
|||||||
var NIP05_REGEX = regexp.MustCompile(`^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$`)
|
var NIP05_REGEX = regexp.MustCompile(`^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$`)
|
||||||
|
|
||||||
type WellKnownResponse struct {
|
type WellKnownResponse struct {
|
||||||
Names map[string]string `json:"names"`
|
Names map[string]nostr.PubKey `json:"names"`
|
||||||
Relays map[string][]string `json:"relays,omitempty"`
|
Relays map[nostr.PubKey][]string `json:"relays,omitempty"`
|
||||||
NIP46 map[string][]string `json:"nip46,omitempty"`
|
NIP46 map[nostr.PubKey][]string `json:"nip46,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsValidIdentifier(input string) bool {
|
func IsValidIdentifier(input string) bool {
|
||||||
@@ -40,17 +40,12 @@ func QueryIdentifier(ctx context.Context, fullname string) (*nostr.ProfilePointe
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkeyh, ok := result.Names[name]
|
pubkey, ok := result.Names[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no entry for name '%s'", name)
|
return nil, fmt.Errorf("no entry for name '%s'", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey, err := nostr.PubKeyFromHex(pubkeyh)
|
relays, _ := result.Relays[pubkey]
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("got an invalid public key '%s'", pubkeyh)
|
|
||||||
}
|
|
||||||
|
|
||||||
relays, _ := result.Relays[pubkeyh]
|
|
||||||
return &nostr.ProfilePointer{
|
return &nostr.ProfilePointer{
|
||||||
PublicKey: pubkey,
|
PublicKey: pubkey,
|
||||||
Relays: relays,
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
@@ -38,11 +40,11 @@ func TestParse(t *testing.T) {
|
|||||||
func TestQuery(t *testing.T) {
|
func TestQuery(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expectedKey string
|
expectedKey nostr.PubKey
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{"fiatjaf.com", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", false},
|
{"fiatjaf.com", nostr.MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), false},
|
||||||
{"htlc@fiatjaf.com", "f9dd6a762506260b38a2d3e5b464213c2e47fa3877429fe9ee60e071a31a07d7", false},
|
{"htlc@fiatjaf.com", nostr.MustPubKeyFromHex("f9dd6a762506260b38a2d3e5b464213c2e47fa3877429fe9ee60e071a31a07d7"), false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@@ -51,7 +53,32 @@ func TestQuery(t *testing.T) {
|
|||||||
assert.Error(t, err, "expected error for input: %s", test.input)
|
assert.Error(t, err, "expected error for input: %s", test.input)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(t, err, "did not expect error for input: %s", test.input)
|
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