Skip to content

Commit

Permalink
Improve Api usability (cont II) (#28)
Browse files Browse the repository at this point in the history
* Nip64 (Relay List)

* Rename `XOnlyPubKey` to `Author`

* Rename `Key` by `SecretKey` and `Author` ny `AuthorId`
  • Loading branch information
lontivero authored Jan 21, 2024
1 parent 0e56988 commit 77e0385
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 113 deletions.
8 changes: 4 additions & 4 deletions Nostra.Client/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ let displayResponse (contacts : Map<byte[], Contact>) (addContact: ContactKey ->

EventId.toBytes eventId
else
XOnlyPubKey.toBytes event.PubKey
Author.toBytes event.PubKey
let maybeContact = contacts |> Map.tryFind contactKey
let author = maybeContact
|> Option.map (fun c -> c.metadata.displayName |> Option.defaultValue c.metadata.name)
Expand Down Expand Up @@ -154,7 +154,7 @@ let Main args =
| Some [c] -> c, StdIn.read "Message"
| Some (c::msgs) -> c, msgs |> List.head

let channel = Shareable.decodeNpub channel' |> Option.map (fun pubkey -> EventId (XOnlyPubKey.toBytes pubkey) ) |> Option.get
let channel = Shareable.decodeNpub channel' |> Option.map (fun pubkey -> EventId (Author.toBytes pubkey) ) |> Option.get
let user = User.load userFilePath
let event = Event.createChannelMessage channel message |> Event.sign user.secret
publish event user.relays
Expand Down Expand Up @@ -207,7 +207,7 @@ let Main args =

let unknownAuthors =
user.subscribedAuthors
|> List.notInBy XOnlyPubKey.equals knownAuthors
|> List.notInBy Author.equals knownAuthors

let filterMetadata =
match unknownAuthors with
Expand All @@ -223,7 +223,7 @@ let Main args =
|> List.map (fun c ->
(match c.key with
| Channel e -> EventId.toBytes e
| Author p -> XOnlyPubKey.toBytes p) , c )
| Author p -> Author.toBytes p) , c )
|> Map.ofList

let addContact contactKey metadata =
Expand Down
34 changes: 16 additions & 18 deletions Nostra.Client/User.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ module List =
|> List.filter (fun i1 -> not (List.exists (predicate i1) list2))


type Author = XOnlyPubKey
type Channel = EventId

type Metadata = {
Expand All @@ -28,45 +27,45 @@ type Relay = {
proxy : Uri Option
}
type ContactKey =
| Author of XOnlyPubKey
| Author of AuthorId
| Channel of Channel

type Contact = {
key : ContactKey
metadata : Metadata
}
type User = {
secret : ECPrivKey
secret : SecretKey
metadata : Metadata
relays : Relay list
contacts : Contact list
subscribedAuthors : Author list
subscribedAuthors : AuthorId list
subscribedChannels : Channel list
}

module Author =
module Decode =
let author : Decoder<Author> =
let shareableAuthor : Decoder<AuthorId> =
Decode.string
|> Decode.map Shareable.decodeNpub
|> Decode.andThen (Decode.ofOption "Not a valid bech32 author")

module Encode =
let author pubkey =
pubkey |> Shareable.encodeNpub |> Encode.string
let shareableAuthor author =
author |> Shareable.encodeNpub |> Encode.string

module ContactKey =
open Author
module Encode =
let contactKey = function
| Author author -> Encode.author author
| Author author -> Encode.shareableAuthor author
| Channel channel -> Encode.eventId channel

module Decode =
let contactKey : Decoder<ContactKey> =
Decode.oneOf [
Decode.eventId |> Decode.map ContactKey.Channel
Decode.author |> Decode.map ContactKey.Author
Decode.shareableAuthor |> Decode.map ContactKey.Author
]

module Metadata =
Expand Down Expand Up @@ -97,7 +96,6 @@ module Metadata =
Encode.object (mandatoryFields @ picture @ about @ displayName @ nip05)

module Contact =
open Author
open Metadata
open ContactKey

Expand All @@ -119,7 +117,7 @@ module User =
open Metadata

let createUser name displayName about picture nip05 =
let secret = Key.createNewRandom ()
let secret = SecretKey.createNewRandom ()
{
secret = secret
metadata = {
Expand All @@ -145,7 +143,7 @@ module User =
proxy = get.Optional.Field "proxy" Decode.uri
})

let secret : Decoder<ECPrivKey> =
let secret : Decoder<SecretKey> =
Decode.string
|> Decode.map Shareable.decodeNsec
|> Decode.andThen (Decode.ofOption "Not a valid bech32 secret")
Expand All @@ -159,7 +157,7 @@ module User =
metadata = get.Required.Field "metadata" Decode.metadata
relays = get.Required.Field "relays" (Decode.list relay)
contacts = get.Required.Field "contacts" (Decode.list Decode.contact)
subscribedAuthors = get.Required.Field "subscribed_authors" (Decode.list Decode.author)
subscribedAuthors = get.Required.Field "subscribed_authors" (Decode.list Decode.shareableAuthor)
subscribedChannels = get.Required.Field "subscribed_channels" (Decode.list channel)
})

Expand All @@ -184,7 +182,7 @@ module User =
"metadata", Encode.metadata user.metadata
"relays", Encode.list (List.map relay user.relays)
"contacts", Encode.list (List.map Encode.contact user.contacts)
"subscribed_authors", Encode.list (List.map Encode.author user.subscribedAuthors)
"subscribed_authors", Encode.list (List.map Encode.shareableAuthor user.subscribedAuthors)
"subscribed_channels", Encode.list (List.map channel user.subscribedChannels)
]

Expand All @@ -211,13 +209,13 @@ module User =
let contacts = { key = key; metadata = metadata }:: user.contacts
{ user with contacts = List.distinctBy (fun c -> c.key) contacts }

let subscribeAuthors (authors : Author list) user =
let authors' = List.distinctBy XOnlyPubKey.toBytes (user.subscribedAuthors @ authors)
let subscribeAuthors (authors : AuthorId list) user =
let authors' = List.distinctBy Author.toBytes (user.subscribedAuthors @ authors)
{ user with subscribedAuthors = authors' }

let unsubscribeAuthors (authors : Author list) user =
let unsubscribeAuthors (authors : AuthorId list) user =
let authors' = user.subscribedAuthors
|> List.notInBy (fun a1 a2 -> XOnlyPubKey.toBytes a1 = XOnlyPubKey.toBytes a2) authors
|> List.notInBy (fun a1 a2 -> Author.toBytes a1 = Author.toBytes a2) authors
{ user with subscribedAuthors = authors' }

let subscribeChannels (channels : Channel list) user =
Expand Down
6 changes: 3 additions & 3 deletions Nostra.Relay/Database.fs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ let handleParameterizedReplacement connection author kind createdAt dtag=
let saveEvent connection (preprocessedEvent: StoredEvent) = asyncResult {
let event = preprocessedEvent.Event
let (EventId eventId) = event.Id
let (XOnlyPubKey xOnlyPubkey) = event.PubKey
let author = xOnlyPubkey.ToBytes()
let (AuthorId author) = event.PubKey
let author = author.ToBytes()
let kind = int event.Kind
let createdAt = event.CreatedAt
let dtag = Tag.findByKey "d" preprocessedEvent.Event.Tags |> List.tryHead |> Option.defaultValue ""
Expand All @@ -180,7 +180,7 @@ let saveEvent connection (preprocessedEvent: StoredEvent) = asyncResult {
return! save connection eventId author preprocessedEvent |> AsyncResult.ignore
}

let deleteEvents connection (XOnlyPubKey author) eventIds =
let deleteEvents connection (AuthorId author) eventIds =
let eventIdsParameters = String.Join(",", eventIds |> List.mapi (fun i _ -> $"@event_hash{i}"))
connection
|> Sql.executeTransactionAsync [
Expand Down
2 changes: 1 addition & 1 deletion Nostra.Relay/EventStore.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open Nostra
open Nostra.Relay

type EventSaver = StoredEvent -> Async<Result<unit, exn>>
type EventsDeleter = XOnlyPubKey -> string list -> Async<Result<int list, exn>>
type EventsDeleter = AuthorId -> string list -> Async<Result<int list, exn>>
type EventsFetcher = Request.Filter list -> System.DateTime -> Async<Result<SerializedEvent list, exn>>

type EventStore = {
Expand Down
4 changes: 2 additions & 2 deletions Nostra.Relay/MessageProcessing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type EventProcessingError =

let preprocessEvent (event : Event) serializedEvent =
let (EventId eventId) = event.Id
let (XOnlyPubKey xOnlyPubkey) = event.PubKey
let pubkey = xOnlyPubkey.ToBytes()
let (AuthorId author) = event.PubKey
let pubkey = author.ToBytes()

{
Event = event
Expand Down
16 changes: 8 additions & 8 deletions Nostra.Tests/Bech32.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type ``Nip19 Bech32-Shareable entities``(output:ITestOutputHelper) =

[<Fact>]
let ``Encode/Decode nsec`` () =
let secKey = Key.createNewRandom()
let secKey = SecretKey.createNewRandom()
NSec secKey
|> encodeDecode
|> function
Expand All @@ -30,13 +30,13 @@ type ``Nip19 Bech32-Shareable entities``(output:ITestOutputHelper) =

[<Fact>]
let ``Encode/Decode npub`` () =
let secKey = Key.createNewRandom()
let pubkey = XOnlyPubKey (Key.getPubKey secKey)
NPub pubkey
let secKey = SecretKey.createNewRandom()
let author = SecretKey.getPubKey secKey
NPub author
|> encodeDecode
|> function
| NPub decodedPubKey ->
should equal (XOnlyPubKey.toBytes decodedPubKey) (XOnlyPubKey.toBytes pubkey)
should equal (Author.toBytes decodedPubKey) (Author.toBytes author)
| _ -> failwith "The entity is not a npub"

[<Fact>]
Expand All @@ -52,8 +52,8 @@ type ``Nip19 Bech32-Shareable entities``(output:ITestOutputHelper) =
[<Fact>]
let ``Encode/Decode nprofile`` () =
let nprofile =
let pubkey = XOnlyPubKey.parse "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" |> Result.requiresOk
NProfile(pubkey, [
let author = Author.parse "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" |> Result.requiresOk
NProfile(author, [
"wss://r.x.com"
"wss://djbas.sadkb.com"
])
Expand All @@ -73,7 +73,7 @@ type ``Nip19 Bech32-Shareable entities``(output:ITestOutputHelper) =
NEvent(
EventId (Utils.fromHex "08a193492c7fb27ab1d95f258461e4a0dfc7f52bccd5e022746cb28418ef4905"),
["wss://nostr.mom"],
Some (XOnlyPubKey.parse "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" |> Result.requiresOk),
Some (Author.parse "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52" |> Result.requiresOk),
Some Kind.Text
)
|> encodeDecode
Expand Down
10 changes: 5 additions & 5 deletions Nostra.Tests/ClientTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ type ``Client tests``(output:ITestOutputHelper, fixture: EchoServerFixture) =
[<Fact>]
let ``Can receive encrypted messages`` () = async {
let! send, receive = Client.createClient fixture.Port
let secret = Key.createNewRandom ()
let pubkey = Key.getPubKey secret |> XOnlyPubKey
let event = Event.createEncryptedDirectMessage pubkey secret "hello" |> Event.sign secret
let secret = SecretKey.createNewRandom ()
let author = SecretKey.getPubKey secret
let event = Event.createEncryptedDirectMessage author secret "hello" |> Event.sign secret
do! send (createRelayMessage "sid" (Event.serialize event))

event.Content |> should not' (equal "hello", "The note is not encrypted")
Expand All @@ -50,7 +50,7 @@ type ``Client tests``(output:ITestOutputHelper, fixture: EchoServerFixture) =
[<Fact>]
let ``Can receive a valid event from relay`` () = async {
let! send, receive = Client.createClient fixture.Port
let event = Event.createNote "Welcome" |> Event.sign (Key.createNewRandom())
let event = Event.createNote "Welcome" |> Event.sign (SecretKey.createNewRandom())
do! send (createRelayMessage "sid" (Event.serialize event))

let! msg = receive
Expand All @@ -62,7 +62,7 @@ type ``Client tests``(output:ITestOutputHelper, fixture: EchoServerFixture) =
[<Fact>]
let ``Can detect invalid (non-authentic) events`` () = async {
let! send, receive = Client.createClient fixture.Port
let event = Event.createNote "Welcome" |> Event.sign (Key.createNewRandom())
let event = Event.createNote "Welcome" |> Event.sign (SecretKey.createNewRandom())
let modifiedEvent = { event with Content = event.Content.Replace("Welcome","Bienvenido") }
let serializedModifiedEvent = modifiedEvent |> Event.serialize
do! send (createRelayMessage "sid" serializedModifiedEvent)
Expand Down
20 changes: 10 additions & 10 deletions Nostra.Tests/PayloadEncryptionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ type ``Nip44 Payload encryption``(output:ITestOutputHelper) =

[<Fact>]
let ``Basic example`` () =
let me = Key.createNewRandom ()
let you = Key.createNewRandom ()
let yourPk = you |> Key.getPubKey |> XOnlyPubKey
let me = SecretKey.createNewRandom ()
let you = SecretKey.createNewRandom ()
let yourPk = you |> SecretKey.getPubKey
let sharedKey = Event.sharedKey yourPk me
let conversationKey = EncryptedPayload.conversationKey sharedKey
let nonce = RandomNumberGenerator.GetBytes 32
Expand All @@ -36,8 +36,8 @@ type ``Nip44 Payload encryption``(output:ITestOutputHelper) =
|> fun x -> x["get_conversation_key"]
|> Seq.map (fun x ->
[|
x |> readHex "sec1" |> ECPrivKey.Create |> box
x |> readHex "pub2" |> ECXOnlyPubKey.Create |> XOnlyPubKey |> box
x |> readHex "sec1" |> ECPrivKey.Create |> SecretKey |> box
x |> readHex "pub2" |> ECXOnlyPubKey.Create |> AuthorId |> box
x |> readHex "conversation_key" |> box
|])

Expand Down Expand Up @@ -67,8 +67,8 @@ type ``Nip44 Payload encryption``(output:ITestOutputHelper) =
|> fun x -> x["encrypt_decrypt"]
|> Seq.map (fun x ->
[|
x |> readHex "sec1" |> ECPrivKey.Create |> box
x |> readHex "sec2" |> ECPrivKey.Create |> box
x |> readHex "sec1" |> ECPrivKey.Create |> SecretKey |> box
x |> readHex "sec2" |> ECPrivKey.Create |> SecretKey |> box
x |> readHex "conversation_key" |> box
x |> readHex "nonce" |> box
x.Value<string>("plaintext") |> box
Expand All @@ -77,7 +77,7 @@ type ``Nip44 Payload encryption``(output:ITestOutputHelper) =

[<Theory>]
[<MemberData(nameof(ConversationKeys))>]
let ``Conversation Key (valid)`` (sec1: ECPrivKey) (pub2 : XOnlyPubKey) (expectedConversationKey : byte[]) =
let ``Conversation Key (valid)`` (sec1: SecretKey) (pub2 : AuthorId) (expectedConversationKey : byte[]) =
let sharedKey = Event.sharedKey pub2 sec1
let conversationKey = EncryptedPayload.conversationKey sharedKey
should equal expectedConversationKey conversationKey
Expand All @@ -95,8 +95,8 @@ type ``Nip44 Payload encryption``(output:ITestOutputHelper) =

[<Theory>]
[<MemberData(nameof(EncryptDecrypts))>]
let ``Encryption and decryption (valid)`` (sec1 : ECPrivKey) (sec2 : ECPrivKey) (expectedConversationKey : byte[]) (nonce : byte[]) (expectedPlainText : string) (expectedPayload : string) =
let sharedKey = Event.sharedKey (sec2 |> Key.getPubKey |> XOnlyPubKey) sec1
let ``Encryption and decryption (valid)`` (sec1 : SecretKey) (sec2 : SecretKey) (expectedConversationKey : byte[]) (nonce : byte[]) (expectedPlainText : string) (expectedPayload : string) =
let sharedKey = Event.sharedKey (sec2 |> SecretKey.getPubKey) sec1
let conversationKey = EncryptedPayload.conversationKey sharedKey
should equal expectedConversationKey conversationKey
let payload = EncryptedPayload.encrypt conversationKey expectedPlainText nonce
Expand Down
4 changes: 2 additions & 2 deletions Nostra.Tests/RelaySimpleTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type ``Relay Accept Events``(output:ITestOutputHelper, fixture:RelayFixture) =
[<Fact>]
let ``Can receive a valid event from relay`` () = async {
let! send, receive = Client.createClient fixture.Port
let event = Event.createNote "Welcome" |> Event.sign (Key.createNewRandom())
let event = Event.createNote "Welcome" |> Event.sign (SecretKey.createNewRandom())
do! send $"""["EVENT",{Event.serialize event}]"""

let! msg = receive
Expand All @@ -45,7 +45,7 @@ type ``Relay Accept Events``(output:ITestOutputHelper, fixture:RelayFixture) =
[<Fact>]
let ``Can detect invalid (non-authentic) events`` () = async {
let! send, receive = Client.createClient fixture.Port
let event = Event.createNote "Welcome" |> Event.sign (Key.createNewRandom())
let event = Event.createNote "Welcome" |> Event.sign (SecretKey.createNewRandom())
let modifiedEvent = { event with Content = event.Content.Replace("Welcome","Bienvenido") }
let serializedModifiedEvent = modifiedEvent |> Event.serialize
do! send ("""["EVENT",""" + serializedModifiedEvent + "]")
Expand Down
8 changes: 4 additions & 4 deletions Nostra.Tests/TestingFramework.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Connection = {
type User = {
SentEvents : Event ResizeArray
ReceivedEvents : Event ResizeArray
Secret : ECPrivKey
Secret : SecretKey
Connection : Connection option
}

Expand Down Expand Up @@ -46,7 +46,7 @@ let ``given`` user : TestStep =
fun ctx ->
let alreadyExists, knownUser = ctx.Users.TryGetValue(user)
if not alreadyExists then
let secret = Key.createNewRandom ()
let secret = SecretKey.createNewRandom ()
ctx.Users.Add (user, { Secret = secret; SentEvents = ResizeArray<Event>(); ReceivedEvents = ResizeArray<Event>(); Connection = None })
async { return { ctx with CurrentUser = user } }

Expand Down Expand Up @@ -120,8 +120,8 @@ let latest n : FilterFactory =
let eventsFrom who : FilterFactory =
fun ctx ->
let user = ctx.Users[who]
let pubkey = user.Secret |> Key.getPubKey |> fun x -> x.ToBytes() |> Utils.toHex
$"""{{"authors": ["{pubkey}"]}}"""
let author = user.Secret |> SecretKey.getPubKey |> fun x -> Author.toBytes x |> Utils.toHex
$"""{{"authors": ["{author}"]}}"""

let ``send event`` eventFactory : TestStep =
fun ctx -> async {
Expand Down
Loading

0 comments on commit 77e0385

Please sign in to comment.