diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fe5239 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/rust/target diff --git a/README.md b/README.md index 2f12ed0..48999ec 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -# chat_proto \ No newline at end of file +# chat_proto +![State](https://img.shields.io/badge/State-WIP-red) + + +This repository contains the core implementation of the $CHAT_PROTO types. + +To make them easy to use, implementations in Rust and Nim are provided developers can focus on building. + + +## Structure + +- **proto:** Protobuf definitions for Core and Conversation types +- **nim:** nimble package for generated types in nim +- **rust:** cargo crate for generated types in rust +- **specs:** current home of the specifications - these will likely be moved out of the repo. They are currently in lockstep with the type definitions which minimizes desync. + +## Related Repositories + +- [Nim POC](https://github.com/waku-org/nim-chat-poc/tree/jazzz/inbox) - This is a demo of the types being consumed in`nim` + - Importing packages from a monorepo appears to be broken in `nimble`, as a short term work around. The `.proto` files have been embedded in the POC, and will be removed once resolved + +- [Rust POC](https://github.com/jazzz/umbra) - This is demo of the types being consumed in `rust` + diff --git a/nim/.devcontainer/devcontainer.json b/nim/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8a86859 --- /dev/null +++ b/nim/.devcontainer/devcontainer.json @@ -0,0 +1,14 @@ +{ + "name": "WapTypes", + "image": "nimlang/nim", + "customizations": { + "vscode": { + "extensions": [ + "NimLang.nimlang", //Nim lang extension + "vadimcn.vscode-lldb", //for Nim debug + "streetsidesoftware.code-spell-checker" //spell checker + ] + } + }, + "postCreateCommand": "./.devcontainer/post_create.sh" +} \ No newline at end of file diff --git a/nim/.devcontainer/post_create.sh b/nim/.devcontainer/post_create.sh new file mode 100755 index 0000000..8581b5a --- /dev/null +++ b/nim/.devcontainer/post_create.sh @@ -0,0 +1 @@ +nimble install nimlangserver diff --git a/nim/.gitignore b/nim/.gitignore new file mode 100644 index 0000000..2ca3f1f --- /dev/null +++ b/nim/.gitignore @@ -0,0 +1,2 @@ +nimble.develop +nimble.paths diff --git a/nim/chat_proto.nim b/nim/chat_proto.nim new file mode 100644 index 0000000..740163e --- /dev/null +++ b/nim/chat_proto.nim @@ -0,0 +1,19 @@ +import protobuf_serialization +import protobuf_serialization/proto_parser + +import_proto3 "../proto/umbra/encryption.proto" +import_proto3 "../proto/umbra/envelope.proto" +import_proto3 "../proto/umbra/inbox.proto" +import_proto3 "../proto/umbra/reliability.proto" + +export protobuf_serialization + +# TODO: Do the Objects have to be listed manually? +export EncryptedPayload +export HistoryEntry +export InboxV1Frame +export ReliablePayload +export UmbraEnvelopeV1 + + + diff --git a/nim/chat_proto.nimble b/nim/chat_proto.nimble new file mode 100644 index 0000000..706c68e --- /dev/null +++ b/nim/chat_proto.nimble @@ -0,0 +1,14 @@ +# Package +packageName = "chat_proto" +version = "0.1.0" +author = "jazzz" +description = "Type definitions for the chat protocol" +license = "MIT" +# bin = @["chat_proto"] +# installDirs = @["../proto"] + + +# Dependencies +requires "nim >= 2.0.14" +requires "chronicles" +requires "protobuf_serialization" diff --git a/nim/config.nims b/nim/config.nims new file mode 100644 index 0000000..8ee48d2 --- /dev/null +++ b/nim/config.nims @@ -0,0 +1,4 @@ +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/nim/tests/config.nims b/nim/tests/config.nims new file mode 100644 index 0000000..80091ff --- /dev/null +++ b/nim/tests/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") diff --git a/nim/tests/test_encoding.nim b/nim/tests/test_encoding.nim new file mode 100644 index 0000000..a1ad7ba --- /dev/null +++ b/nim/tests/test_encoding.nim @@ -0,0 +1,13 @@ +import unittest + +import chat_proto + +suite "Type Encoding Tests": + test "HistoryEntry roundtrip": + let x = chat_proto.HistoryEntry(message_id: "12345", retrieval_hint: @[1'u8, 2, 3, 255]) + let encoded = Protobuf.encode(x) + let decoded = Protobuf.decode(encoded, HistoryEntry) + + check x.message_id == decoded.message_id + check x.retrieval_hint == decoded.retrieval_hint + check x == decoded diff --git a/proto/wap/common_frames.proto b/proto/wap/common_frames.proto new file mode 100644 index 0000000..972f58e --- /dev/null +++ b/proto/wap/common_frames.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package wap.common_frames; + +message ContentFrame { + uint32 domain = 1; + uint32 tag = 2; + bytes bytes = 3; +} diff --git a/proto/wap/conversations/private_v1.proto b/proto/wap/conversations/private_v1.proto new file mode 100644 index 0000000..d2022a2 --- /dev/null +++ b/proto/wap/conversations/private_v1.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package wap.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; + // .... + } +} diff --git a/proto/wap/encryption.proto b/proto/wap/encryption.proto new file mode 100644 index 0000000..1d42123 --- /dev/null +++ b/proto/wap/encryption.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package wap.encryption; + + +message EncryptedPayload { + oneof encryption { + NoiseKN noise_KN = 3; + } +} + +message NoiseKN { + bytes encrypted_bytes = 1; + bytes ephemeral_pubkey = 2; +} diff --git a/proto/wap/envelope.proto b/proto/wap/envelope.proto new file mode 100644 index 0000000..6a961d4 --- /dev/null +++ b/proto/wap/envelope.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package wap.envelope; + + +/////////////////////////////////////////////////////////////////////////////// +// Payload Framing Messages +/////////////////////////////////////////////////////////////////////////////// + +message WapEnvelopeV1 { + + string conversation_hint = 1; + uint64 salt = 2; + + bytes payload = 5; +} diff --git a/proto/wap/inbox.proto b/proto/wap/inbox.proto new file mode 100644 index 0000000..6f85db5 --- /dev/null +++ b/proto/wap/inbox.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package wap.inbox; + +import "invite.proto"; + +message InboxV1Frame { + string recipient = 1; + oneof frame_type { + invite.InvitePrivateV1 invite_private_v1 = 10; + } +} diff --git a/proto/wap/invite.proto b/proto/wap/invite.proto new file mode 100644 index 0000000..d0e7fd3 --- /dev/null +++ b/proto/wap/invite.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package wap.invite; + +message InvitePrivateV1 { + repeated string participants = 1; +} diff --git a/proto/wap/reliability.proto b/proto/wap/reliability.proto new file mode 100644 index 0000000..f71f9b2 --- /dev/null +++ b/proto/wap/reliability.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package wap.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; + } diff --git a/rust/.devcontainer/devcontainer.json b/rust/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b53d567 --- /dev/null +++ b/rust/.devcontainer/devcontainer.json @@ -0,0 +1,12 @@ +{ + "name": "Rust-WapTypes", + "image": "mcr.microsoft.com/devcontainers/rust:latest", + "customizations": { + "vscode": { + "extensions": [ + "pbkit.vscode-pbkit" + ] + } + }, + "postCreateCommand": "bash -i .devcontainer/postcreate.sh" +} \ No newline at end of file diff --git a/rust/.devcontainer/postcreate.sh b/rust/.devcontainer/postcreate.sh new file mode 100755 index 0000000..0783b61 --- /dev/null +++ b/rust/.devcontainer/postcreate.sh @@ -0,0 +1,2 @@ +sudo apt-get update +sudo apt-get install -y protobuf-compiler diff --git a/rust/.vscode/settings.json b/rust/.vscode/settings.json new file mode 100644 index 0000000..cc90acf --- /dev/null +++ b/rust/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "[rust]": { + "editor.formatOnSave": true + } +} \ No newline at end of file diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..a2181ec --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,593 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "prettyplease" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes 1.10.1", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf", +] + +[[package]] +name = "protoc" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0218039c514f9e14a5060742ecd50427f8ac4f85a6dc58f2ddb806e318c55ee" +dependencies = [ + "log", + "which", +] + +[[package]] +name = "protoc-rust" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f8a182bb17c485f20bdc4274a8c39000a61024cfe461c799b50fec77267838" +dependencies = [ + "protobuf", + "protobuf-codegen", + "protoc", + "tempfile", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wap-types" +version = "0.0.2-dev" +dependencies = [ + "bytes 0.4.12", + "prost", + "prost-build", + "protoc-rust", +] + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] diff --git a/rust/cargo.toml b/rust/cargo.toml new file mode 100644 index 0000000..b42facc --- /dev/null +++ b/rust/cargo.toml @@ -0,0 +1,18 @@ +[workspace] + +members = [ + "wap-types" +] + +default-members = [ + "wap-types" +] + +resolver = "3" + +[workspace.package] +version = "0.0.1-dev" + +[workspace.dependencies] + + diff --git a/rust/wap-types/Cargo.toml b/rust/wap-types/Cargo.toml new file mode 100644 index 0000000..798e686 --- /dev/null +++ b/rust/wap-types/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wap-types" +edition = "2024" +version = "0.0.2-dev" + +[dependencies] +prost = "0.13.5" +bytes = "0.4" + +[build-dependencies] +prost-build = "0.13.5" +protoc-rust = "2" diff --git a/rust/wap-types/build.rs b/rust/wap-types/build.rs new file mode 100644 index 0000000..55883a9 --- /dev/null +++ b/rust/wap-types/build.rs @@ -0,0 +1,43 @@ +extern crate protoc_rust; + +use std::env; +use std::path::PathBuf; + +fn get_proto_dir() -> PathBuf { + println!( + "Manifest dir: {:?}", + env::var("CARGO_MANIFEST_DIR").unwrap() + ); + // protos are stored in ../../proto/ + let mut proto_root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + proto_root_dir.pop(); + proto_root_dir.pop(); + proto_root_dir.push("proto"); + proto_root_dir.push("wap"); + + println!("proto_dir: {:?}", proto_root_dir); + proto_root_dir +} + +fn main() { + let mut config = prost_build::Config::new(); + + // Create mod file to maintain import hierarchy + config.include_file("mod.rs"); + + config + .compile_protos( + &[ + "envelope.proto", + "common_frames.proto", + "conversations/private_v1.proto", + "encryption.proto", + "inbox.proto", + "invite.proto", + "reliability.proto" + ], + // set proto_path + &[get_proto_dir().to_str().unwrap()], + ) + .unwrap(); +} diff --git a/rust/wap-types/src/lib.rs b/rust/wap-types/src/lib.rs new file mode 100644 index 0000000..6145d84 --- /dev/null +++ b/rust/wap-types/src/lib.rs @@ -0,0 +1,24 @@ +pub mod payload; + +pub use payload::types::wap::*; + +#[cfg(test)] + +mod tests { + use super::*; + use prost::Message; + + #[test] + fn test_private_v1_roundtrip() { + convos::private_v1::PrivateV1Frame { + conversation_id: "conversationId".to_string(), + frame_type: Some(convos::private_v1::private_v1_frame::FrameType::Content( + common_frames::ContentFrame { + domain: 0, + tag: 0, + bytes: "Hello, World!".to_string().encode_to_vec(), + }, + )), + }; + } +} diff --git a/rust/wap-types/src/payload.rs b/rust/wap-types/src/payload.rs new file mode 100644 index 0000000..453f118 --- /dev/null +++ b/rust/wap-types/src/payload.rs @@ -0,0 +1,94 @@ +pub mod types { + include!(concat!(env!("OUT_DIR"), "/mod.rs")); +} + +use prost::Message; +pub use types::wap::*; + +use crate::{ + convos::private_v1::{PrivateV1Frame, private_v1_frame}, + encryption::EncryptedPayload, + envelope::WapEnvelopeV1, + inbox::{InboxV1Frame, inbox_v1_frame}, +}; + +impl PrivateV1Frame { + pub fn new(conversation_id: String, frame: private_v1_frame::FrameType) -> Self { + PrivateV1Frame { + conversation_id, + frame_type: Some(frame), + } + } +} + +impl InboxV1Frame { + pub fn new(recipient: String, frame: inbox_v1_frame::FrameType) -> Self { + InboxV1Frame { + recipient: recipient, + // conversation_id, + frame_type: Some(frame), + } + } +} + +pub trait ToEnvelope { + fn to_envelope(self, conversation_id: String, salt: u64) -> WapEnvelopeV1; +} + +impl ToEnvelope for EncryptedPayload { + fn to_envelope(self, conversation_id: String, salt: u64) -> WapEnvelopeV1 { + WapEnvelopeV1 { + conversation_hint: conversation_id, // TODO + salt, + payload: self.encode_to_vec(), // Avoid allocation here? + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use prost::Message; + + use crate::reliability::ReliablePayload; + use crate::common_frames::ContentFrame; + + + #[test] + fn test_private_v1_roundtrip() { + let text = "Hello, World!".to_string(); + + let msg = PrivateV1Frame { + conversation_id: "conversationId".to_string(), + frame_type: Some(private_v1_frame::FrameType::Content( + ContentFrame { + domain: 0, + tag: 0, + bytes: text.encode_to_vec(), + }, + )), + }; + + let reliable = ReliablePayload { + message_id: "msg_id".into(), + channel_id: msg.conversation_id.clone(), + lamport_timestamp: 0, + causal_history: vec![], + bloom_filter: vec![1, 2, 3, 4], + content: msg.encode_to_vec(), + }; + + let buf = reliable.encode_to_vec(); + + let reliable_msg = ReliablePayload::decode(&*buf).unwrap(); + + let msg_from_bytes = + PrivateV1Frame::decode(&*reliable_msg.content) + .expect("Failed to decode message"); + + assert_eq!( + msg, msg_from_bytes, + "Encoded and decoded messages should match" + ); + } +} diff --git a/specs/.md b/specs/.md new file mode 100644 index 0000000..6bb1414 --- /dev/null +++ b/specs/.md @@ -0,0 +1,122 @@ +--- +title: Waku Application Protocol +name: A private decentralized messaging protocol for multiple usecases. +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +# Abstract + +This specification defines a modular communication protocol designed to provide a privacy focused approach to common messaging patterns. The protocol employs a lean root layer that defers to independent sub-protocols for defining communication behavior, with versioning as a first-class concept for handling protocol evolution. + +# Background / Rationale / Motivation + +Traditionally communication protocols face several critical challenges as they grow. + +Different communication scenarios have vastly different requirements. A protocol optimized for high-volume public broadcasts performs poorly for intimate encrypted conversations, and vice versa. Monolithic protocols cannot optimize for these diverse use cases simultaneously. + +Once widely deployed, communication protocols become difficult to modify. Even minor changes can break compatibility across clients. As a result versioning becomes a complex negotiation problem, which makes deploying protocol updates difficult. At each stage protocols become increasingly difficult to modify, which slows down forward progress and eventually leads to ossification. + +A preferred approach would be to practice resiliency at the protocol level by focusing on versioning from the beginning. + +# Theory / Semantics + +This protocol is a lean coordination layer which provides the backbone for smaller independent "Conversation" sub-protocols. Conversation protocols completely define a pathway for communication. This root protocol provides common functionality to support a wide array of communication use cases, and the remaining functionality is deferred to Conversation protocols to define. + +```mermaid +flowchart TD + P(%PROTO) --> C1(Conversation) + P --> C2(Conversation) + P --> C3(....) + style C3 fill:#FFFFFF,stroke:#,stroke-width:1px,stroke-dasharray: 5 1 +``` + +## Conversations +ConversationTypes are standalone methods to send and receive messages. While a full service "chat" protocol needs to provide additional features such as identity management, message history, backups and versioning, conversations are more narrowly scoped. + +They can be created permissionlessly within the %PROTO, as there is no required registration. Developers are free to use any given conversation type as long as all intended participants support it. + +ConversationTypes MUST adhere to the [Conversations](./conversations.md) specification in order to maintain compatibility, however network adoption is not a requirement. + + + +## Versioning + +Incompatible versions of a conversation protocol result in distinct conversationTypes (E.g. PrivateV1 vs PrivateV3). The opinionated strategy is to allow multiplexing at the conversation level, rather than requiring the entire network of clients to run the same version. + +Individual conversationTypes can implement functionality to migrate participants to a conversation, but that is deferred to contributors. By making this a conversationType issue, different use cases can choose a system that works for them. + +Developers wishing to interop with other projects will need to ensure they have overlapping support of ConversationTypes. + + +## Default Inbox +There exists a circular dependency in initializing a Conversation. Conversations require an established conversation to send an invitation, yet at the beginning no channel exists between contacts. + +To resolve this all clients MUST implement a default [Inbox](./inbox.md) to receive initial messages. The default inbox allows clients to discover new conversations asynchronously without prior coordination. By listening in a static location. + +The default inbox MUST be configured with the parameters: +- **inbox_addr:** `client_address` +- **discriminator:** "default" + +As the clients address is directly linked to the content_topic there is some metadata leakage, and this pathway SHOULD only be used as a last resort. + +## Envelopes +As clients can support multiple sub-protocols simultaneously, a mechanism is required to determine how decode messages. + +All payloads sent MUST be deterministically decodable. Deterministic decoding means that clients can always classify a envelope as 1 of 3 states: Readable, BadlyFormed, Addressed to someone else. To support this %PROTO uses an Envelope to link encrypted payloads with the information required to process them. As this payload is sent in the clear the payloads cannot contain any identifiable information. + + + +### Conversation Hinting +Conversation_ids are sufficient to allow clients to lookup the required encryption parameters and decrypt a message. However this would leak Conversation metadata. Instead a hint is used which allows clients to check if this message is of interest. + +Messages with an unknown hint can be safely disregarded. + +ConversationHints are computed by using a salted hash of the `conversationId`. specifically defined as `lowercase_hex(blake2s(salt || conversation_id))` +[TODO: Should conversations define their own hinting?] + + + +## Wire Format Specification / Syntax + +The wire format is specified using protocol buffers v3. + +```protobuf + +message WapEnvelopeV1 { + + string conversation_hint = 1; + uint32 salt = 2; + + EncryptedBytes encrypted_bytes = 3; +} + + +``` + +## Implementation Suggestions (optional) + +### User level Conversations + +Application developers should maintain standalone identifiers for user-level conversations that are separate from the protocol-level conversation_id. A single logical conversation from the user's perspective may utilize multiple ConversationTypes over time. As versions are considered different types, the underlying relationship is many to one. By maintaining application-level conversation identifiers, developers can provide users with consistent conversation continuity while the underlying protocol mechanisms handle version transitions and security upgrades transparently. + + +## (Further Optional Sections) + + +## Security/Privacy Considerations + +Messages inherit the privacy and security properties of the ConversationType used to send them. Please refer to the corresponding specifications when analyzing properties. + +### Default Inbox Privacy +Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. diff --git a/specs/conversations.md b/specs/conversations.md new file mode 100644 index 0000000..bbaf3c1 --- /dev/null +++ b/specs/conversations.md @@ -0,0 +1,89 @@ +--- +title: CONVERSATIONS +name: Conversation based communications +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +# Abstract + +This specification outlines the requirements for defining a ConversationType + +# Background / Rationale / Motivation + +Modern communication systems require a reliable and flexible way to establish and manage conversations between participants. +While simple message-passing protocols exist, they lack the ability to manage state across a stream of messages. + +The Conversations Protocol addresses this gap by defining a mechanism to establish long term communication sessions in decentralized environments. + + +# Theory / Semantics + +A ConversationType is a specification which defines a method for clients to communicate. +Each ConversationType defines its own encryption, encoding, and message types which are necessary for operation. + +A Conversation is an instance of a particular ConversationType which contains the associated state such as membership, encryption parameters, names etc. + +## Requirements +To be considered valid, every ConversationType specification is required to define the operation completely. + +A ConversationType MUST define which encryption scheme is used +A ConversationType MUST define which Frames are used. +A ConversationType MUST define which encoding is used. +A ConversationType MUST define which content topics are valid places to receive messages. +A ConversationType MUST define how to generate conversation_ids + +A ConversationType SHOULD define membership requirements and limitations. +A ConversationType SHOULD define privacy and security guarantees. +A ConversationType SHOULD maintain a deterministic decoding tree. + +## ConversationType Identifiers +ConversationTypes are identified by the title of the specification. This allows developers to lookup the associated specification. + +[TODO: This doesn't make any sense, as its been mentioned that new versions of a conversationType are distinct types. Which is it? ] + +E.g. inbox.md -> InboxV1 + +## Conversation Identifiers + +conversation_ids allow for the efficient lookup of encryption state. + +Care should be taken to ensure that conversation_ids do not conflict. +[TODO: Should more guidance be added later? e,g mandating a format to ensure uniqueness between conversationTypes -- ///] + +[TODO: touch on the nuance of generating conversation_ids from participant lists?] + + +## Framing +To disambiguate between different logical layers, payload types sent by a Conversation are referred to as `Frames`. +Conversations are free to determine which frames are needed for their specific use cases. + + + + +## Encryption +Conversation types are free to choose which ever encryption mechanism works best for their application. +[TODO: Expand on recomendations] + + +## Content Topics +Content topics are how ConversationTypes define where an how messages are discovered by participants. + +When developing new ConversationTypes contributors should consider: +- Privacy impacts of the chosen topic policy. +- Channel binding and the impacts on message security. + + +## Security/Privacy Considerations + +This approach puts heavy requirements on ConversationTypes to build their own cryptography without providing much guidance. Finding mechanisms that provide safety while maintaining the flexibility should be prioritized in follow up work. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. diff --git a/specs/inbox.md b/specs/inbox.md new file mode 100644 index 0000000..adb400f --- /dev/null +++ b/specs/inbox.md @@ -0,0 +1 @@ +Moved to WakuSpecs - https://github.com/waku-org/specs/pull/72