mirror of
https://github.com/logos-messaging/nim-chat-poc.git
synced 2026-01-08 09:03:11 +00:00
Initial commit
This commit is contained in:
parent
2683ad825c
commit
939532fecd
@ -26,4 +26,4 @@ requires "nimchacha20poly1305" # TODO: remove
|
|||||||
requires "confutils >= 0.1.0"
|
requires "confutils >= 0.1.0"
|
||||||
requires "eth >= 0.8.0"
|
requires "eth >= 0.8.0"
|
||||||
requires "regex >= 0.26.3"
|
requires "regex >= 0.26.3"
|
||||||
requires "web3 >= 0.7.0"
|
requires "web3 >= 0.7.0"
|
||||||
|
|||||||
9
protos/common_frames.proto
Normal file
9
protos/common_frames.proto
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.common_frames;
|
||||||
|
|
||||||
|
message ContentFrame {
|
||||||
|
uint32 domain = 1;
|
||||||
|
uint32 tag = 2;
|
||||||
|
bytes bytes = 3;
|
||||||
|
}
|
||||||
28
protos/conversations/group_v1.proto
Normal file
28
protos/conversations/group_v1.proto
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.convos.group_v1;
|
||||||
|
|
||||||
|
import "base.proto";
|
||||||
|
import "common_frames.proto";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message ConversationInvite_GroupV1 {
|
||||||
|
repeated string participants = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message GroupV1Frame {
|
||||||
|
// SDS like information: Message ID and channel_id extracted for utility
|
||||||
|
string message_id = 2;
|
||||||
|
string channel_id = 3; // Channel_id is associated with a set of participants
|
||||||
|
// This conflicts with conversation based encryption,
|
||||||
|
// need to ensure the derived sender is a valid participant
|
||||||
|
base.ReliabilityInfo reliability_info = 10;
|
||||||
|
|
||||||
|
oneof frame_type {
|
||||||
|
common_frames.ContentFrame content = 100;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
22
protos/conversations/private_v1.proto
Normal file
22
protos/conversations/private_v1.proto
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.convos.private_v1;
|
||||||
|
|
||||||
|
import "common_frames.proto";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message Placeholder {
|
||||||
|
uint32 counter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrivateV1Frame {
|
||||||
|
string conversation_id = 1;
|
||||||
|
|
||||||
|
oneof frame_type {
|
||||||
|
common_frames.ContentFrame content = 10;
|
||||||
|
Placeholder placeholder = 11;
|
||||||
|
// ....
|
||||||
|
}
|
||||||
|
}
|
||||||
24
protos/encryption.proto
Normal file
24
protos/encryption.proto
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.encryption;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: This also encompasses plaintexts, is there a better name?
|
||||||
|
// Alternatives: ???
|
||||||
|
message EncryptedPayload {
|
||||||
|
|
||||||
|
oneof encryption {
|
||||||
|
encryption.Plaintext plaintext = 1;
|
||||||
|
encryption.Ecies ecies = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Plaintext {
|
||||||
|
bytes payload=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ecies {
|
||||||
|
bytes encrypted_bytes=1;
|
||||||
|
bytes ephemeral_pubkey = 2;
|
||||||
|
bytes tag = 3;
|
||||||
|
}
|
||||||
16
protos/envelope.proto
Normal file
16
protos/envelope.proto
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.envelope;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Payload Framing Messages
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
message UmbraEnvelopeV1 {
|
||||||
|
|
||||||
|
string conversation_hint = 1;
|
||||||
|
uint64 salt = 2;
|
||||||
|
|
||||||
|
bytes payload = 5;
|
||||||
|
}
|
||||||
12
protos/inbox.proto
Normal file
12
protos/inbox.proto
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.inbox;
|
||||||
|
|
||||||
|
import "invite.proto";
|
||||||
|
|
||||||
|
message InboxV1Frame {
|
||||||
|
string recipient = 1;
|
||||||
|
oneof frame_type {
|
||||||
|
invite.InvitePrivateV1 invite_private_v1 = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
protos/invite.proto
Normal file
7
protos/invite.proto
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.invite;
|
||||||
|
|
||||||
|
message InvitePrivateV1 {
|
||||||
|
repeated string participants = 1;
|
||||||
|
}
|
||||||
23
protos/reliability.proto
Normal file
23
protos/reliability.proto
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package umbra.reliability;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SDS Payloads
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
message HistoryEntry {
|
||||||
|
string message_id = 1; // Unique identifier of the SDS message, as defined in `Message`
|
||||||
|
bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS
|
||||||
|
// message; For example, A Waku deterministic message hash or routing payload hash
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReliablePayload {
|
||||||
|
string message_id = 2;
|
||||||
|
string channel_id = 3;
|
||||||
|
int32 lamport_timestamp = 10;
|
||||||
|
repeated HistoryEntry causal_history = 11;
|
||||||
|
bytes bloom_filter = 12;
|
||||||
|
// Optional field causes errors in nim protobuf generation. Removing for now as optional is implied anways.
|
||||||
|
bytes content = 20;
|
||||||
|
}
|
||||||
175
src/client.nim
Normal file
175
src/client.nim
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import tables
|
||||||
|
import identity
|
||||||
|
import crypto
|
||||||
|
import proto_types
|
||||||
|
import std/times
|
||||||
|
import utils
|
||||||
|
import dev
|
||||||
|
import inbox
|
||||||
|
|
||||||
|
import conversations/private_v1
|
||||||
|
|
||||||
|
import secp256k1
|
||||||
|
import chronicles
|
||||||
|
|
||||||
|
type KeyEntry* = object
|
||||||
|
keytype: string
|
||||||
|
keypair: SkKeyPair
|
||||||
|
timestamp: int64
|
||||||
|
|
||||||
|
|
||||||
|
type SupportedConvoTypes* = Inbox | PrivateV1
|
||||||
|
|
||||||
|
type
|
||||||
|
ConvoType* = enum
|
||||||
|
InboxV1Type, PrivateV1Type
|
||||||
|
|
||||||
|
ConvoWrapper* = object
|
||||||
|
case convo_type*: ConvoType
|
||||||
|
of InboxV1Type:
|
||||||
|
inboxV1*: Inbox
|
||||||
|
of PrivateV1Type:
|
||||||
|
privateV1*: PrivateV1
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Client* = object
|
||||||
|
ident: Identity
|
||||||
|
key_store: Table[string, KeyEntry] # Keyed by HexEncoded Public Key
|
||||||
|
conversations: Table[string, ConvoWrapper] # Keyed by conversation ID
|
||||||
|
|
||||||
|
|
||||||
|
proc process_invite*(self: Client, invite: InvitePrivateV1)
|
||||||
|
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Constructors
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
proc initClient*(name: string): Client =
|
||||||
|
|
||||||
|
|
||||||
|
var c = Client(ident: createIdentity(name),
|
||||||
|
key_store: initTable[string, KeyEntry](),
|
||||||
|
conversations: initTable[string, ConvoWrapper]())
|
||||||
|
|
||||||
|
let default_inbox = initInbox(c.ident.getAddr(), proc(
|
||||||
|
x: InvitePrivateV1) = c.process_invite(x))
|
||||||
|
|
||||||
|
c.conversations[conversation_id_for(c.ident.getPubkey(
|
||||||
|
))] = ConvoWrapper(convo_type: InboxV1Type, inboxV1: default_inbox)
|
||||||
|
|
||||||
|
result = c
|
||||||
|
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Parameter Access
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
proc getClientAddr*(self: Client): string =
|
||||||
|
result = self.ident.getAddr()
|
||||||
|
|
||||||
|
proc default_inbox_conversation_id*(self: Client): string =
|
||||||
|
## Returns the default inbox address for the client.
|
||||||
|
result = conversation_id_for(self.ident.getPubkey())
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Methods
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
# proc addConversation*(c: var Client, convo: Conversation) =
|
||||||
|
# if not c.conversations.hasKey(convo.getConvoId()):
|
||||||
|
# c.conversations[convo.getConvoId()] = convo
|
||||||
|
# else:
|
||||||
|
# echo "Conversation with ID ", convo.getConvoId(), " already exists."
|
||||||
|
|
||||||
|
|
||||||
|
proc createIntroBundle*(self: var Client): IntroBundle =
|
||||||
|
## Generates an IntroBundle for the client, which includes
|
||||||
|
## the required information to send a message.
|
||||||
|
|
||||||
|
# Create Ephemeral keypair, save it in the key store
|
||||||
|
let ephemeral_keypair = generate_keypair()
|
||||||
|
self.key_store[ephemeral_keypair.pubkey.toHexCompressed()] = KeyEntry(
|
||||||
|
keytype: "ephemeral",
|
||||||
|
keypair: ephemeral_keypair,
|
||||||
|
timestamp: getTime().toUnix(),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = IntroBundle(
|
||||||
|
ident: @(self.ident.getPubkey().toRawCompressed()),
|
||||||
|
ephemeral: @(ephemeral_keypair.pubkey.toRawCompressed()),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc createPrivateConvo*(self: Client, intro_bundle: IntroBundle): TransportMessage =
|
||||||
|
## Creates a private conversation with the given Invitebundle.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let res_pubkey = SkPublicKey.fromRaw(intro_bundle.ident)
|
||||||
|
if res_pubkey.isErr:
|
||||||
|
raise newException(ValueError, "Invalid public key in intro bundle.")
|
||||||
|
let dest_pubkey = res_pubkey.get()
|
||||||
|
let convo_id = "/convo/inbox/" & dest_pubkey.getAddr()
|
||||||
|
|
||||||
|
let dst_convo_topic = topic_inbox(dest_pubkey.get_addr())
|
||||||
|
|
||||||
|
let invite = InvitePrivateV1(
|
||||||
|
participants: @[self.ident.getAddr(), dest_pubkey.get_addr()],
|
||||||
|
)
|
||||||
|
let env = wrap_env(encrypt(InboxV1Frame(invite_private_v1: invite,
|
||||||
|
recipient: "")), convo_id)
|
||||||
|
|
||||||
|
# Create a new conversation
|
||||||
|
# let convo_id = self.ident.getAddr() & "-" & raya_bundle.ident.toHexCompressed()
|
||||||
|
# let new_convo = Conversation(id: convo_id, participants: @[self.ident,
|
||||||
|
# Identity(name: "Raya", keypair: SkKeyPair.fromRawCompressed(
|
||||||
|
# raya_bundle.ident))])
|
||||||
|
|
||||||
|
# Add the conversation to the client's store
|
||||||
|
# self.addConversation(new_convo)
|
||||||
|
|
||||||
|
# Return the invite (or any other relevant data)
|
||||||
|
# return new_convo
|
||||||
|
|
||||||
|
return sendTo(dst_convo_topic, encode(env))
|
||||||
|
|
||||||
|
proc get_conversation(self: Client,
|
||||||
|
conversation_hint: string): Result[Option[ConvoWrapper], string] =
|
||||||
|
|
||||||
|
# TODO: Implementing Hinting
|
||||||
|
if not self.conversations.hasKey(conversation_hint):
|
||||||
|
ok(none(ConvoWrapper))
|
||||||
|
else:
|
||||||
|
ok(some(self.conversations[conversation_hint]))
|
||||||
|
|
||||||
|
|
||||||
|
proc recv*(self: var Client, transport_message: TransportMessage): seq[
|
||||||
|
TransportMessage] =
|
||||||
|
## Reveives a incomming payload, decodes it, and processes it.
|
||||||
|
let res_env = decode(transport_message.payload, UmbraEnvelopeV1)
|
||||||
|
if res_env.isErr:
|
||||||
|
raise newException(ValueError, "Failed to decode UmbraEnvelopeV1: " & res_env.error)
|
||||||
|
let env = res_env.get()
|
||||||
|
|
||||||
|
let res_convo = self.get_conversation(env.conversation_hint)
|
||||||
|
if res_convo.isErr:
|
||||||
|
raise newException(ValueError, "Failed to get conversation: " &
|
||||||
|
res_convo.error)
|
||||||
|
|
||||||
|
let convo = res_convo.get()
|
||||||
|
if not convo.isSome:
|
||||||
|
debug "No conversation found", hint = env.conversation_hint
|
||||||
|
return
|
||||||
|
|
||||||
|
let inbox = convo.get().inboxV1
|
||||||
|
|
||||||
|
let res = inbox.handle_incomming_frame(transport_message.topic, env.payload)
|
||||||
|
if res.isErr:
|
||||||
|
warn "Failed to handle incoming frame: ", error = res.error
|
||||||
|
return @[]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc process_invite*(self: Client, invite: InvitePrivateV1) =
|
||||||
|
debug "Callback Invoked", invite = invite
|
||||||
4
src/conversations/private_v1.nim
Normal file
4
src/conversations/private_v1.nim
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
type
|
||||||
|
PrivateV1* = object
|
||||||
|
# Placeholder for PrivateV1 conversation type
|
||||||
|
name*: string
|
||||||
33
src/crypto.nim
Normal file
33
src/crypto.nim
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import proto_types
|
||||||
|
|
||||||
|
import secp256k1
|
||||||
|
import std/[sysrand]
|
||||||
|
export secp256k1
|
||||||
|
|
||||||
|
|
||||||
|
type KeyPair* = SkKeyPair
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc encrypt_plain*[T: EncryptableTypes](frame: T): EncryptedPayload =
|
||||||
|
return EncryptedPayload(
|
||||||
|
plaintext: Plaintext(payload: encode(frame)),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc decrypt_plain*[T: EncryptableTypes](ciphertext: Plaintext, t: typedesc[
|
||||||
|
T]): Result[T, string] =
|
||||||
|
|
||||||
|
let obj = decode(ciphertext.payload, T)
|
||||||
|
if obj.isErr:
|
||||||
|
return err("Protobuf decode failed: " & obj.error)
|
||||||
|
|
||||||
|
result = ok(obj.get())
|
||||||
|
|
||||||
|
proc generate_keypair*(): KeyPair =
|
||||||
|
var rng: Rng = urandom
|
||||||
|
let res = SkKeyPair.random(rng)
|
||||||
|
if res.isErr:
|
||||||
|
raise newException(ValueError, "Failed to generate keypair: ")
|
||||||
|
|
||||||
|
result = res.get()
|
||||||
|
|
||||||
6
src/dev.nim
Normal file
6
src/dev.nim
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
## Utilties for development and debugging
|
||||||
|
|
||||||
|
proc dir*[T](obj: T) =
|
||||||
|
echo "Object of type: ", T
|
||||||
|
for name, value in fieldPairs(obj):
|
||||||
|
echo " ", name, ": ", value
|
||||||
30
src/identity.nim
Normal file
30
src/identity.nim
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import crypto
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Identity* = object
|
||||||
|
name: string
|
||||||
|
keypair: KeyPair
|
||||||
|
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Constructors
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
proc createIdentity*(name: string): Identity =
|
||||||
|
let keypair = generate_keypair()
|
||||||
|
result = Identity(name: name, keypair: keypair)
|
||||||
|
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Parameter Access
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
proc getAddr*(self: Identity): string =
|
||||||
|
result = get_addr(self.keypair.pubkey)
|
||||||
|
|
||||||
|
|
||||||
|
proc getPubkey*(self: Identity): SkPublicKey =
|
||||||
|
result = self.keypair.pubkey
|
||||||
65
src/inbox.nim
Normal file
65
src/inbox.nim
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import crypto
|
||||||
|
import proto_types
|
||||||
|
import utils
|
||||||
|
import dev
|
||||||
|
|
||||||
|
import chronicles
|
||||||
|
|
||||||
|
|
||||||
|
type InviteCallback* = proc(invite: InvitePrivateV1): void
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
Inbox* = object
|
||||||
|
inbox_addr: string
|
||||||
|
invite_callback: InviteCallback
|
||||||
|
|
||||||
|
proc initInbox*(inbox_addr: string, invite_callback: InviteCallback): Inbox =
|
||||||
|
## Initializes an Inbox object with the given address and invite callback.
|
||||||
|
return Inbox(inbox_addr: inbox_addr, invite_callback: invite_callback)
|
||||||
|
|
||||||
|
|
||||||
|
proc encrypt*(frame: InboxV1Frame): EncryptedPayload =
|
||||||
|
return encrypt_plain(frame)
|
||||||
|
|
||||||
|
proc wrap_env*(payload: EncryptedPayload, convo_id: string): UmbraEnvelopeV1 =
|
||||||
|
let bytes = encode(payload)
|
||||||
|
let salt = generateSalt()
|
||||||
|
|
||||||
|
return UmbraEnvelopeV1(
|
||||||
|
payload: bytes,
|
||||||
|
salt: salt,
|
||||||
|
conversation_hint: convo_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
proc conversation_id_for*(pubkey: SkPublicKey): string =
|
||||||
|
## Generates a conversation ID based on the public key.
|
||||||
|
return "/convo/inbox/" & pubkey.get_addr()
|
||||||
|
|
||||||
|
# TODO derive this from instance of Inbox
|
||||||
|
proc topic_inbox*(client_addr: string): string =
|
||||||
|
return "/inbox/v1/" & client_addr
|
||||||
|
|
||||||
|
|
||||||
|
proc handle_incomming_frame*(self: Inbox, topic: string, bytes: seq[
|
||||||
|
byte]): Result[int, string] =
|
||||||
|
# TODO: Can this fail?
|
||||||
|
let res = decode(bytes, EncryptedPayload)
|
||||||
|
if res.isErr:
|
||||||
|
return err("Failed to decode payload: " & res.error)
|
||||||
|
|
||||||
|
let encbytes = res.get()
|
||||||
|
|
||||||
|
# NOTE! in nim_protobuf_serializaiton OneOf fields are not exclusive, and all fields are default initialized.
|
||||||
|
if encbytes.plaintext == Plaintext():
|
||||||
|
return err("Incorrect Encryption Type")
|
||||||
|
|
||||||
|
let res_frame = decrypt_plain(encbytes.plaintext, InboxV1Frame)
|
||||||
|
if res_frame.isErr:
|
||||||
|
return err("Failed to decrypt frame: " & res_frame.error)
|
||||||
|
let frame = res_frame.get()
|
||||||
|
|
||||||
|
self.invite_callback(frame.invite_private_v1)
|
||||||
|
ok(0)
|
||||||
|
|
||||||
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
chronicles,
|
chronicles,
|
||||||
chronos,
|
chronos,
|
||||||
@ -28,3 +29,39 @@ proc main(): Future[void] {.async.} =
|
|||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
waitFor main()
|
waitFor main()
|
||||||
|
|
||||||
|
# import client
|
||||||
|
# import chronicles
|
||||||
|
# import proto_types
|
||||||
|
|
||||||
|
# proc log(transport_message: TransportMessage) =
|
||||||
|
# ## Log the transport message
|
||||||
|
# info "Transport Message:", topic = transport_message.topic,
|
||||||
|
# payload = transport_message.payload
|
||||||
|
|
||||||
|
# proc demo() =
|
||||||
|
|
||||||
|
# # Initalize Clients
|
||||||
|
# var saro = initClient("Saro")
|
||||||
|
# var raya = initClient("Raya")
|
||||||
|
|
||||||
|
# # # Exchange Contact Info
|
||||||
|
# let raya_bundle = raya.createIntroBundle()
|
||||||
|
|
||||||
|
# # Create Conversation
|
||||||
|
# let invite = saro.createPrivateConvo(raya_bundle)
|
||||||
|
# invite.log()
|
||||||
|
# let msgs = raya.recv(invite)
|
||||||
|
|
||||||
|
# # raya.convos()[0].sendText("Hello Saro, this is Raya!")
|
||||||
|
|
||||||
|
|
||||||
|
# when isMainModule:
|
||||||
|
# echo("Starting ChatPOC...")
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# demo()
|
||||||
|
# except Exception as e:
|
||||||
|
# error "Crashed ", error = e.msg
|
||||||
|
|
||||||
|
# echo("Finished...")
|
||||||
|
|||||||
57
src/proto_types.nim
Normal file
57
src/proto_types.nim
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Can this be an external package? It would be preferable to have these types
|
||||||
|
# easy to import and use.
|
||||||
|
|
||||||
|
import protobuf_serialization # This import is needed or th macro will not work
|
||||||
|
import protobuf_serialization/proto_parser
|
||||||
|
import results
|
||||||
|
|
||||||
|
export protobuf_serialization
|
||||||
|
|
||||||
|
import_proto3 "../protos/inbox.proto"
|
||||||
|
# import_proto3 "../protos/invite.proto" // Import3 follows protobuf includes so this will result in a redefinition error
|
||||||
|
import_proto3 "../protos/encryption.proto"
|
||||||
|
import_proto3 "../protos/envelope.proto"
|
||||||
|
|
||||||
|
type EncryptableTypes = InboxV1Frame | EncryptedPayload
|
||||||
|
|
||||||
|
export EncryptedPayload
|
||||||
|
export InboxV1Frame
|
||||||
|
|
||||||
|
export EncryptableTypes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc encode*(frame: object): seq[byte] =
|
||||||
|
## Encodes the frame into a byte sequence using Protobuf serialization.
|
||||||
|
result = Protobuf.encode(frame)
|
||||||
|
|
||||||
|
|
||||||
|
proc decode*[T: object] (bytes: seq[byte], proto: typedesc[
|
||||||
|
T]): Result[T, string] =
|
||||||
|
## Encodes the frame into a byte sequence using Protobuf serialization.
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = ok(Protobuf.decode(bytes, proto))
|
||||||
|
except ProtobufError as e:
|
||||||
|
result = err("Failed to decode payload: " & e.msg)
|
||||||
|
|
||||||
|
type
|
||||||
|
IntroBundle {.proto3.} = object
|
||||||
|
ident* {.fieldNumber: 1.}: seq[byte]
|
||||||
|
ephemeral* {.fieldNumber: 2.}: seq[byte]
|
||||||
|
|
||||||
|
|
||||||
|
export IntroBundle
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TransportMessage {.proto3.} = object
|
||||||
|
topic* {.fieldNumber: 1.}: string
|
||||||
|
payload* {.fieldNumber: 2.}: seq[byte]
|
||||||
|
|
||||||
|
|
||||||
|
proc sendTo*(topic: string, payload: seq[byte]): TransportMessage =
|
||||||
|
result = TransportMessage(topic: topic, payload: payload)
|
||||||
|
|
||||||
|
export TransportMessage
|
||||||
@ -1,6 +1,18 @@
|
|||||||
import std/times
|
|
||||||
import waku/waku_core
|
import waku/waku_core
|
||||||
|
import std/[random, times]
|
||||||
|
import crypto
|
||||||
|
import blake2
|
||||||
|
|
||||||
proc getTimestamp*(): Timestamp =
|
proc getTimestamp*(): Timestamp =
|
||||||
result = waku_core.getNanosecondTime(getTime().toUnix())
|
result = waku_core.getNanosecondTime(getTime().toUnix())
|
||||||
|
|
||||||
|
proc generateSalt*(): uint64 =
|
||||||
|
randomize()
|
||||||
|
result = 0
|
||||||
|
for i in 0 ..< 8:
|
||||||
|
result = result or (uint64(rand(255)) shl (i * 8))
|
||||||
|
|
||||||
|
proc get_addr*(pubkey: SkPublicKey): string =
|
||||||
|
# TODO: Needs Spec
|
||||||
|
result = getBlake2b(pubkey.toHexCompressed(), 4, "")
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user