From baa0767c263fa25fdd8d90a42d13f1f1fffce804 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 2 Dec 2019 16:34:05 +0100 Subject: [PATCH] Handle membership update message This commit does a few things: 1) Handle membership updates using protobuf and adds the relevant endpoints. 2) Store in memory a map of chats + contacts for faster lookups, which are then flushed to disk on each update 3) Validate incoming messages Sorry for the large pr, but you know, v1 :) --- VERSION | 2 +- go.mod | 11 +- go.sum | 19 +- multiaccounts/migrations/bindata.go | 6 +- protocol/chat.go | 67 +- protocol/chat_group_proxy.go | 21 +- protocol/go.mod | 6 +- protocol/go.sum | 76 +- protocol/group_chat_system_messages.go | 88 ++ protocol/message_handler.go | 72 +- protocol/message_handler_test.go | 78 ++ protocol/message_processor.go | 64 +- protocol/message_processor_test.go | 13 +- protocol/message_validator.go | 51 + protocol/message_validator_test.go | 260 ++++ protocol/messenger.go | 1093 +++++++++++------ protocol/messenger_test.go | 436 ++++--- protocol/migrations/migrations.go | 8 +- protocol/persistence.go | 190 ++- protocol/persistence_legacy.go | 2 +- protocol/persistence_legacy_test.go | 56 - .../application_metadata_message.pb.go | 144 +++ .../application_metadata_message.proto | 22 + protocol/protobuf/chat_message.pb.go | 326 +++++ .../protobuf/chat_message.proto | 40 +- .../protobuf/membership_update_message.pb.go | 227 ++++ .../protobuf/membership_update_message.proto | 40 + protocol/protobuf/message.pb.go | 421 ------- protocol/protobuf/service.go | 2 +- protocol/v1/clock.go | 8 +- protocol/v1/decoder.go | 124 +- protocol/v1/encoder.go | 46 +- protocol/v1/membership_update_message.go | 462 +++---- protocol/v1/membership_update_message_test.go | 244 ++-- protocol/v1/message.go | 30 +- protocol/v1/message_test.go | 8 - protocol/v1/status_message.go | 44 +- services/shhext/api.go | 54 +- services/shhext/service.go | 2 +- .../github.com/elastic/gosigar/CHANGELOG.md | 11 - vendor/github.com/elastic/gosigar/README.md | 1 - .../elastic/gosigar/sigar_darwin.go | 12 + .../elastic/gosigar/sigar_darwin_386.go | 18 - .../elastic/gosigar/sigar_darwin_amd64.go | 18 - .../elastic/gosigar/sigar_interface.go | 42 +- .../elastic/gosigar/sigar_windows.go | 22 +- .../elastic/gosigar/sys/windows/doc.go | 6 - .../gosigar/sys/windows/syscall_windows.go | 5 + .../gosigar/sys/windows/zsyscall_windows.go | 10 +- .../gballet/go-libpcsclite/error.go | 182 +-- .../gballet/go-libpcsclite/winscard.go | 22 +- .../golang-migrate/migrate/v4/.golangci.yml | 2 +- .../golang-migrate/migrate/v4/.travis.yml | 18 +- .../golang-migrate/migrate/v4/Dockerfile | 2 +- .../golang-migrate/migrate/v4/Makefile | 2 +- .../golang-migrate/migrate/v4/README.md | 4 +- .../golang-migrate/migrate/v4/go.mod | 2 - .../gomarkdown/markdown/ast/node.go | 15 + .../gomarkdown/markdown/parser/inline.go | 35 + .../gomarkdown/markdown/parser/parser.go | 9 + .../status-im/status-go/protocol/chat.go | 67 +- .../status-go/protocol/chat_group_proxy.go | 21 +- .../status-im/status-go/protocol/go.mod | 10 +- .../status-im/status-go/protocol/go.sum | 76 +- .../protocol/group_chat_system_messages.go | 88 ++ .../status-go/protocol/message_handler.go | 72 +- .../status-go/protocol/message_processor.go | 64 +- .../status-go/protocol/message_validator.go | 51 + .../status-im/status-go/protocol/messenger.go | 1093 +++++++++++------ .../protocol/migrations/migrations.go | 8 +- .../status-go/protocol/persistence.go | 190 ++- .../status-go/protocol/persistence_legacy.go | 2 +- .../application_metadata_message.pb.go | 144 +++ .../application_metadata_message.proto | 22 + .../protocol/protobuf/chat_message.pb.go | 326 +++++ .../protocol/protobuf/chat_message.proto | 40 +- .../protobuf/membership_update_message.pb.go | 227 ++++ .../protobuf/membership_update_message.proto | 40 + .../status-go/protocol/protobuf/message.pb.go | 421 ------- .../status-go/protocol/protobuf/service.go | 2 +- .../status-im/status-go/protocol/v1/clock.go | 8 +- .../status-go/protocol/v1/decoder.go | 124 +- .../status-go/protocol/v1/encoder.go | 46 +- .../protocol/v1/membership_update_message.go | 462 +++---- .../status-go/protocol/v1/message.go | 30 +- .../status-go/protocol/v1/status_message.go | 44 +- vendor/golang.org/x/crypto/blowfish/block.go | 159 +++ vendor/golang.org/x/crypto/blowfish/cipher.go | 99 ++ vendor/golang.org/x/crypto/blowfish/const.go | 199 +++ vendor/modules.txt | 22 +- 90 files changed, 5689 insertions(+), 3769 deletions(-) create mode 100644 protocol/group_chat_system_messages.go create mode 100644 protocol/message_handler_test.go create mode 100644 protocol/message_validator.go create mode 100644 protocol/message_validator_test.go create mode 100644 protocol/protobuf/application_metadata_message.pb.go create mode 100644 protocol/protobuf/application_metadata_message.proto create mode 100644 protocol/protobuf/chat_message.pb.go rename vendor/github.com/status-im/status-go/protocol/protobuf/message.proto => protocol/protobuf/chat_message.proto (64%) create mode 100644 protocol/protobuf/membership_update_message.pb.go create mode 100644 protocol/protobuf/membership_update_message.proto delete mode 100644 protocol/protobuf/message.pb.go delete mode 100644 vendor/github.com/elastic/gosigar/sigar_darwin_386.go delete mode 100644 vendor/github.com/elastic/gosigar/sigar_darwin_amd64.go create mode 100644 vendor/github.com/status-im/status-go/protocol/group_chat_system_messages.go create mode 100644 vendor/github.com/status-im/status-go/protocol/message_validator.go create mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.pb.go create mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.proto create mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.pb.go rename protocol/protobuf/message.proto => vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.proto (64%) create mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.pb.go create mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.proto delete mode 100644 vendor/github.com/status-im/status-go/protocol/protobuf/message.pb.go create mode 100644 vendor/golang.org/x/crypto/blowfish/block.go create mode 100644 vendor/golang.org/x/crypto/blowfish/cipher.go create mode 100644 vendor/golang.org/x/crypto/blowfish/const.go diff --git a/VERSION b/VERSION index 88e18b18f..ca75280b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.37.3 +0.38.0 diff --git a/go.mod b/go.mod index d66239576..d7e9dc9e7 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ replace github.com/Sirupsen/logrus v1.4.2 => github.com/sirupsen/logrus v1.4.2 replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1 -replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 +replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/protocol => ./protocol @@ -20,16 +20,12 @@ replace github.com/status-im/status-go/whisper/v6 => ./whisper require ( github.com/beevik/ntp v0.2.0 - github.com/elastic/gosigar v0.10.5 // indirect github.com/ethereum/go-ethereum v1.9.5 - github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/golang-migrate/migrate/v4 v4.7.0 // indirect github.com/golang/mock v1.3.1 - github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/leodido/go-urn v1.2.0 // indirect github.com/lib/pq v1.2.0 - github.com/libp2p/go-libp2p v0.4.0 // indirect + github.com/libp2p/go-libp2p v0.4.0 // indirect; indirect github.com/libp2p/go-libp2p-core v0.2.3 github.com/multiformats/go-multiaddr v0.1.1 github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f @@ -40,7 +36,6 @@ require ( github.com/russolsen/ohyeah v0.0.0-20160324131710-f4938c005315 // indirect github.com/russolsen/same v0.0.0-20160222130632-f089df61f51d // indirect github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a - github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0 // indirect github.com/status-im/migrate/v4 v4.6.2-status.2 github.com/status-im/rendezvous v1.3.0 github.com/status-im/status-go/eth-node v1.0.0 @@ -52,8 +47,6 @@ require ( github.com/syndtr/goleveldb v1.0.0 go.uber.org/zap v1.13.0 golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba - golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 // indirect - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 diff --git a/go.sum b/go.sum index f31db83b3..9574ec0e6 100644 --- a/go.sum +++ b/go.sum @@ -128,9 +128,8 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa h1:o8OuEkracbk3qH6GvlI6XpEN1HTSxkzOG42xZpfDv/s= github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/elastic/gosigar v0.10.4 h1:6jfw75dsoflhBMRdO6QPzQUgLqUYTsQQQRkkcsHsuPo= github.com/elastic/gosigar v0.10.4/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= -github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo= -github.com/elastic/gosigar v0.10.5/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= @@ -144,8 +143,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -172,8 +169,6 @@ github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-migrate/migrate/v4 v4.6.2 h1:LDDOHo/q1W5UDj6PbkxdCv7lv9yunyZHXvxuwDkGo3k= github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= -github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= -github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -282,8 +277,6 @@ github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2vi github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8 h1:VhnqxaTIudc9IWKx8uXRLnpdSb9noCEj+vHacjmhp68= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= -github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -601,10 +594,8 @@ github.com/status-im/go-multiaddr-ethv4 v1.2.0 h1:OT84UsUzTCwguqCpJqkrCMiL4VZ1Sv github.com/status-im/go-multiaddr-ethv4 v1.2.0/go.mod h1:2VQ3C+9zEurcceasz12gPAtmEzCeyLUGPeKLSXYQKHo= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0 h1:5UdlDkkBoPrJfh7zkfoR3X5utJhNs/MCQysK3x0ycgg= -github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 h1:ijC73VP0hucsy/MRn4cmtoQVB1mKdLcvurMYPvmPc4Y= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba h1:Ut2CKuG+L9eWFL7dTEPuLE+RKecUYBkDNhqXSIgY92U= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8= github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= github.com/status-im/rendezvous v1.3.0 h1:7RK/MXXW+tlm0asKm1u7Qp7Yni6AO29a7j8+E4Lbjg4= @@ -727,8 +718,6 @@ golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= -golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -739,8 +728,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/multiaccounts/migrations/bindata.go b/multiaccounts/migrations/bindata.go index 0bf541d19..2bbf13e53 100644 --- a/multiaccounts/migrations/bindata.go +++ b/multiaccounts/migrations/bindata.go @@ -86,7 +86,7 @@ func _0001_accountsDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 0)} + info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0644), modTime: time.Unix(1573806410, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0x61, 0x4c, 0x18, 0xfc, 0xc, 0xdf, 0x5c, 0x1f, 0x5e, 0xd3, 0xbd, 0xfa, 0x12, 0x5e, 0x8d, 0x8d, 0x8b, 0xb9, 0x5f, 0x99, 0x46, 0x63, 0xa5, 0xe3, 0xa6, 0x8a, 0x4, 0xf1, 0x73, 0x8a, 0xe9}} return a, nil } @@ -106,7 +106,7 @@ func _0001_accountsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0001_accounts.up.sql", size: 163, mode: os.FileMode(0644), modTime: time.Unix(1575565817, 0)} + info := bindataFileInfo{name: "0001_accounts.up.sql", size: 163, mode: os.FileMode(0644), modTime: time.Unix(1575903446, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf2, 0xfa, 0x99, 0x8e, 0x96, 0xb3, 0x13, 0x6c, 0x1f, 0x6, 0x27, 0xc5, 0xd2, 0xd4, 0xe0, 0xa5, 0x26, 0x82, 0xa7, 0x26, 0xf2, 0x68, 0x9d, 0xed, 0x9c, 0x3d, 0xbb, 0xdc, 0x37, 0x28, 0xbc, 0x1}} return a, nil } @@ -126,7 +126,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 0)} + info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1573806410, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}} return a, nil } diff --git a/protocol/chat.go b/protocol/chat.go index 0e5e04b21..684e1cea8 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -5,12 +5,23 @@ import ( "crypto/sha1" "encoding/hex" "encoding/json" + "math/rand" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) +var chatColors = []string{ + "#fa6565", // red + "#887af9", // blue + "#FE8F59", // orange + "#7cda00", // green + "#51d0f0", // light-blue + "#d37ef4", // purple +} + type ChatType int const ( @@ -31,9 +42,6 @@ type Chat struct { ChatType ChatType `json:"chatType"` - // Only filled for one to one chats - PublicKey *ecdsa.PublicKey `json:"-"` - // Timestamp indicates the last time this chat has received/sent a message Timestamp int64 `json:"timestamp"` // LastClockValue indicates the last clock value to be used when sending messages @@ -50,7 +58,21 @@ type Chat struct { // Members are the members who have been invited to the group chat Members []ChatMember `json:"members"` // MembershipUpdates is all the membership events in the chat - MembershipUpdates []ChatMembershipUpdate `json:"membershipUpdates"` + MembershipUpdates []v1protocol.MembershipUpdateEvent `json:"membershipUpdateEvents"` +} + +func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) { + // For one to one chatID is an encoded public key + if c.ChatType != ChatTypeOneToOne { + return nil, nil + } + pkey, err := hex.DecodeString(c.ID[2:]) + if err != nil { + return nil, err + } + // Safety check, make sure is well formed + return crypto.UnmarshalPubkey(pkey) + } func (c *Chat) MarshalJSON() ([]byte, error) { @@ -131,30 +153,15 @@ func (c *Chat) updateChatFromProtocolGroup(g *v1protocol.Group) { c.Members = chatMembers // MembershipUpdates - updates := g.Updates() - membershipUpdates := make([]ChatMembershipUpdate, 0, len(updates)) - for _, update := range updates { - membershipUpdate := ChatMembershipUpdate{ - Type: update.Type, - Name: update.Name, - ClockValue: uint64(update.ClockValue), // TODO: get rid of type casting - Signature: update.Signature, - From: update.From, - Member: update.Member, - Members: update.Members, - } - membershipUpdate.setID() - membershipUpdates = append(membershipUpdates, membershipUpdate) - } - c.MembershipUpdates = membershipUpdates + c.MembershipUpdates = g.Events() } // ChatMembershipUpdate represent an event on membership of the chat type ChatMembershipUpdate struct { // Unique identifier for the event ID string `json:"id"` - // Type indicates the kind of event (i.e changed-name, added-member, etc) - Type string `json:"type"` + // Type indicates the kind of event + Type protobuf.MembershipUpdateEvent_EventType `json:"type"` // Name represents the name in the event of changing name events Name string `json:"name,omitempty"` // Clock value of the event @@ -198,11 +205,10 @@ func oneToOneChatID(publicKey *ecdsa.PublicKey) string { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey) Chat { return Chat{ - ID: oneToOneChatID(publicKey), - Name: name, - Active: true, - ChatType: ChatTypeOneToOne, - PublicKey: publicKey, + ID: oneToOneChatID(publicKey), + Name: name, + Active: true, + ChatType: ChatTypeOneToOne, } } @@ -211,14 +217,17 @@ func CreatePublicChat(name string) Chat { ID: name, Name: name, Active: true, + Color: chatColors[rand.Intn(len(chatColors))], ChatType: ChatTypePublic, } } func createGroupChat() Chat { return Chat{ - Active: true, - ChatType: ChatTypePrivateGroupChat, + Active: true, + Color: chatColors[rand.Intn(len(chatColors))], + Timestamp: int64(timestampInMs()), + ChatType: ChatTypePrivateGroupChat, } } diff --git a/protocol/chat_group_proxy.go b/protocol/chat_group_proxy.go index f31fe623d..5e2c44964 100644 --- a/protocol/chat_group_proxy.go +++ b/protocol/chat_group_proxy.go @@ -5,24 +5,5 @@ import ( ) func newProtocolGroupFromChat(chat *Chat) (*v1protocol.Group, error) { - return v1protocol.NewGroup(chat.ID, chatToFlattenMembershipUpdate(chat)) -} - -func chatToFlattenMembershipUpdate(chat *Chat) []v1protocol.MembershipUpdateFlat { - result := make([]v1protocol.MembershipUpdateFlat, len(chat.MembershipUpdates)) - for idx, update := range chat.MembershipUpdates { - result[idx] = v1protocol.MembershipUpdateFlat{ - ChatID: chat.ID, - From: update.From, - Signature: update.Signature, - MembershipUpdateEvent: v1protocol.MembershipUpdateEvent{ - Name: update.Name, - Type: update.Type, - ClockValue: int64(update.ClockValue), // TODO: remove type difference - Member: update.Member, - Members: update.Members, - }, - } - } - return result + return v1protocol.NewGroup(chat.ID, chat.MembershipUpdates) } diff --git a/protocol/go.mod b/protocol/go.mod index 7c2cb486d..ca0400c3c 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -4,7 +4,7 @@ go 1.13 replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7 -replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 +replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/eth-node => ../eth-node @@ -14,11 +14,10 @@ require ( github.com/cenkalti/backoff/v3 v3.0.0 github.com/ethereum/go-ethereum v1.9.5 github.com/golang/protobuf v1.3.2 - github.com/gomarkdown/markdown v0.0.0-20191113114344-af599402d015 + github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba github.com/google/uuid v1.1.1 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/lucasb-eyer/go-colorful v1.0.2 - github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f github.com/pkg/errors v0.8.1 github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a @@ -29,5 +28,4 @@ require ( github.com/stretchr/testify v1.4.0 github.com/vacp2p/mvds v0.0.23 go.uber.org/zap v1.13.0 - golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba ) diff --git a/protocol/go.sum b/protocol/go.sum index 7b6fcca1a..7886435ed 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -34,9 +34,8 @@ github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1: github.com/aristanetworks/goarista v0.0.0-20181002214814-33151c4543a7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20190219163901-728bce664cf5 h1:L0TwgZQo7Mga9im6FvKEZGIvyLE/VG/HI5loz5LpvC0= github.com/aristanetworks/goarista v0.0.0-20190219163901-728bce664cf5/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 h1:tS7jSmwRqSxTnonTRlDD1oHo6Q9YOK4xHS9/v4L56eg= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= -github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 h1:fKnuvQ/O22ZpD7HaJjGQXn/GxOdDJOQFL8bpM8Xe3X8= -github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= @@ -49,6 +48,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190418232430-6867ff32788a/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -124,6 +125,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elastic/gosigar v0.10.4 h1:6jfw75dsoflhBMRdO6QPzQUgLqUYTsQQQRkkcsHsuPo= github.com/elastic/gosigar v0.10.4/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo= +github.com/elastic/gosigar v0.10.5/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= @@ -136,6 +139,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= @@ -145,10 +150,10 @@ github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2i github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -160,6 +165,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-migrate/migrate/v4 v4.6.2 h1:LDDOHo/q1W5UDj6PbkxdCv7lv9yunyZHXvxuwDkGo3k= github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= +github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= +github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -254,6 +261,8 @@ github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2vi github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8 h1:VhnqxaTIudc9IWKx8uXRLnpdSb9noCEj+vHacjmhp68= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= +github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -270,8 +279,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -359,8 +368,14 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5 h1:l16XLUUJ34wIz+RIvLhSwGvLvKyy+W598b135bJN6mg= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -373,8 +388,26 @@ github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVq github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5 h1:1wxmCvTXAifAepIMyF39vZinRw5sbqjPs/UIi93+uik= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f h1:hd3r+uv9DNLScbOrnlj82rBldHQf3XWmCeXAWbw8euQ= github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f/go.mod h1:MyUWrZlB1aI5bs7j9/pJ8ckLLZ4QcCYcNiSbsAW32D4= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -480,21 +513,27 @@ github.com/status-im/doubleratchet v3.0.0+incompatible h1:aJ1ejcSERpSzmWZBgtfYti github.com/status-im/doubleratchet v3.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU= github.com/status-im/go-ethereum v1.9.5-status.6 h1:ytuTO1yBIAuTVRtRQoc2mrdyngtP+XOQ9IHIibbz7/I= github.com/status-im/go-ethereum v1.9.5-status.6/go.mod h1:08JvQWE+IOnAFSe4UD4ACLNe2fDd9XmWMCq5Yzy9mk0= +github.com/status-im/go-ethereum v1.9.5-status.7 h1:DKH1GiF52LwaZaw6YDBliFEgm/JDsbIT+hn7ph6X94Q= github.com/status-im/go-ethereum v1.9.5-status.7/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 h1:ijC73VP0hucsy/MRn4cmtoQVB1mKdLcvurMYPvmPc4Y= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= +github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0 h1:5UdlDkkBoPrJfh7zkfoR3X5utJhNs/MCQysK3x0ycgg= +github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba h1:Ut2CKuG+L9eWFL7dTEPuLE+RKecUYBkDNhqXSIgY92U= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8= github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= +github.com/status-im/rendezvous v1.3.0/go.mod h1:+hzjuP+j/XzLPeF6E50b88pWOTLdTcwjvNYt+Gh1W1s= github.com/status-im/status-go v0.36.0 h1:91qDMJjHv+T3Li9FwxsWQ2JBVcYtvVDT0nGFSMnmM+8= github.com/status-im/status-go v0.36.1 h1:nb9eTq0UQJ57YyTZSl5U05emFT+R4AW8/Bga6ocgOks= +github.com/status-im/status-go v0.37.3 h1:94/bOA8qrEIgWd23mSLN39SwUJwCu2TPQFV2HzSI2ZE= +github.com/status-im/status-go v0.37.3/go.mod h1:9qHQ2+8NS6ivPJS5YbsI3gWkr0t6DWmJzKnr4M7vudw= github.com/status-im/status-go/extkeys v1.0.0 h1:Qyirsoi5Ye5UFfisgPtCjPb/RkBxyK+UsSiEcr2PVlM= github.com/status-im/status-go/extkeys v1.0.0/go.mod h1:GdqJbrcpkNm5ZsSCpp+PdMxnXx+OcRBdm3PI0rs1FpU= +github.com/status-im/status-go/protocol v1.0.1/go.mod h1:LpA7BsaNmj6EOdq7BwuqncewjPqIRHCletZOb2wlWrY= +github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= github.com/status-im/whisper v1.5.2 h1:26NgiKusmPic38eQdtXnaY+iaQ/LuQ3Dh0kCGYT/Uxs= github.com/status-im/whisper v1.5.2/go.mod h1:emrOxzJme0k66QtbbQ2bdd3P8RCdLZ8sTD7SkwH1s2s= -github.com/status-im/whisper v1.6.1 h1:C/T1HQHZfUI2jbccf3yIe8yfkl435I3BILIKeNASJDc= -github.com/status-im/whisper v1.6.1/go.mod h1:lygchT4p9Y1/hR451OhNNqfinvy9EYEDxtXU2T/U30Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= @@ -503,6 +542,7 @@ github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9C github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -604,8 +644,11 @@ golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -616,6 +659,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -629,8 +674,15 @@ golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk= +golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= diff --git a/protocol/group_chat_system_messages.go b/protocol/group_chat_system_messages.go new file mode 100644 index 000000000..cbbd7260d --- /dev/null +++ b/protocol/group_chat_system_messages.go @@ -0,0 +1,88 @@ +package protocol + +import ( + "fmt" + "strings" + "time" + + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/protobuf" + v1protocol "github.com/status-im/status-go/protocol/v1" +) + +var defaultSystemMessagesTranslations = map[protobuf.MembershipUpdateEvent_EventType]string{ + protobuf.MembershipUpdateEvent_CHAT_CREATED: "{{from}} created the group {{name}}", + protobuf.MembershipUpdateEvent_NAME_CHANGED: "{{from}} changed the group's name to {{name}}", + protobuf.MembershipUpdateEvent_MEMBERS_ADDED: "{{from}} has invited {{members}}", + protobuf.MembershipUpdateEvent_MEMBER_JOINED: "{{from}} joined the group", + protobuf.MembershipUpdateEvent_ADMINS_ADDED: "{{from}} has made {{members}} admin", + protobuf.MembershipUpdateEvent_MEMBER_REMOVED: "{{member}} left the group", + protobuf.MembershipUpdateEvent_ADMIN_REMOVED: "{{member}} is not admin anymore", +} + +func tsprintf(format string, params map[string]string) string { + for key, val := range params { + format = strings.Replace(format, "{{"+key+"}}", fmt.Sprintf("%s", val), -1) + } + return fmt.Sprintf(format) +} + +func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) *Message { + var text string + switch e.Type { + case protobuf.MembershipUpdateEvent_CHAT_CREATED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_CHAT_CREATED], map[string]string{"from": "@" + e.From, "name": e.Name}) + case protobuf.MembershipUpdateEvent_NAME_CHANGED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_NAME_CHANGED], map[string]string{"from": "@" + e.From, "name": e.Name}) + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: + + var memberMentions []string + for _, s := range e.Members { + memberMentions = append(memberMentions, "@"+s) + } + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBERS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")}) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_JOINED], map[string]string{"from": "@" + e.From}) + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: + var memberMentions []string + for _, s := range e.Members { + memberMentions = append(memberMentions, "@"+s) + } + text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMINS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")}) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_REMOVED], map[string]string{"member": "@" + e.Members[0]}) + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMIN_REMOVED], map[string]string{"member": "@" + e.Members[0]}) + + } + timestamp := v1protocol.TimestampInMsFromTime(time.Now()) + message := &Message{ + ChatMessage: protobuf.ChatMessage{ + ChatId: e.ChatID, + Text: text, + MessageType: protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + Clock: e.ClockValue, + Timestamp: timestamp, + }, + From: e.From, + WhisperTimestamp: timestamp, + LocalChatID: e.ChatID, + Seen: true, + ID: types.EncodeHex(crypto.Keccak256(e.Signature)), + } + message.PrepareContent() + return message +} + +func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*Message { + var messages []*Message + + for _, e := range events { + messages = append(messages, eventToSystemMessage(e, translations)) + + } + + return messages +} diff --git a/protocol/message_handler.go b/protocol/message_handler.go index b34c21a48..171ad0b0c 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -3,66 +3,44 @@ package protocol import ( "github.com/pkg/errors" + "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) -type persistentMessageHandler struct { - persistence *sqlitePersistence -} - -func newPersistentMessageHandler(persistence *sqlitePersistence) *persistentMessageHandler { - return &persistentMessageHandler{persistence: persistence} -} - // HandleMembershipUpdate updates a Chat instance according to the membership updates. // It retrieves chat, if exists, and merges membership updates from the message. // Finally, the Chat is updated with the new group events. -func (h *persistentMessageHandler) HandleMembershipUpdate(m v1protocol.MembershipUpdateMessage) error { - chat, err := h.chatID(m.ChatID) - switch err { - case errChatNotFound: - group, err := v1protocol.NewGroupWithMembershipUpdates(m.ChatID, m.Updates) +func HandleMembershipUpdate(chat *Chat, m *v1protocol.MembershipUpdateMessage, myIdentity string, translations map[protobuf.MembershipUpdateEvent_EventType]string) (*Chat, []*Message, error) { + if chat == nil { + if len(m.Events) == 0 { + return nil, nil, errors.New("can't create new group chat without events") + } + group, err := v1protocol.NewGroupWithEvents(m.ChatID, m.Events) if err != nil { - return err + return nil, nil, err + } + + // A new chat must contain us + if !group.IsMember(myIdentity) { + return nil, nil, errors.New("can't create a new group chat without us being a member") } newChat := createGroupChat() newChat.updateChatFromProtocolGroup(group) - chat = &newChat - case nil: - existingGroup, err := newProtocolGroupFromChat(chat) - if err != nil { - return errors.Wrap(err, "failed to create a Group from Chat") - } - updateGroup, err := v1protocol.NewGroupWithMembershipUpdates(m.ChatID, m.Updates) - if err != nil { - return errors.Wrap(err, "invalid membership update") - } - merged := v1protocol.MergeFlatMembershipUpdates(existingGroup.Updates(), updateGroup.Updates()) - newGroup, err := v1protocol.NewGroup(chat.ID, merged) - if err != nil { - return errors.Wrap(err, "failed to create a group with new membership updates") - } - chat.updateChatFromProtocolGroup(newGroup) - default: - return err + return &newChat, buildSystemMessages(m.Events, translations), nil } - return h.persistence.SaveChat(*chat) -} - -func (h *persistentMessageHandler) chatID(chatID string) (*Chat, error) { - var chat *Chat - chats, err := h.persistence.Chats() + existingGroup, err := newProtocolGroupFromChat(chat) if err != nil { - return nil, err + return nil, nil, errors.Wrap(err, "failed to create a Group from Chat") } - for _, ch := range chats { - if ch.ID == chatID { - chat = ch - break - } + updateGroup, err := v1protocol.NewGroupWithEvents(m.ChatID, m.Events) + if err != nil { + return nil, nil, errors.Wrap(err, "invalid membership update") } - if chat == nil { - return nil, errChatNotFound + merged := v1protocol.MergeMembershipUpdateEvents(existingGroup.Events(), updateGroup.Events()) + newGroup, err := v1protocol.NewGroup(chat.ID, merged) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create a group with new membership updates") } - return chat, nil + chat.updateChatFromProtocolGroup(newGroup) + return chat, buildSystemMessages(m.Events, translations), nil } diff --git a/protocol/message_handler_test.go b/protocol/message_handler_test.go new file mode 100644 index 000000000..cdb86bb76 --- /dev/null +++ b/protocol/message_handler_test.go @@ -0,0 +1,78 @@ +package protocol + +import ( + v1protocol "github.com/status-im/status-go/protocol/v1" + "github.com/stretchr/testify/suite" + "testing" +) + +func TestEventToSystemMessageSuite(t *testing.T) { + suite.Run(t, new(EventToSystemMessageSuite)) +} + +type EventToSystemMessageSuite struct { + suite.Suite +} + +func (s *EventToSystemMessageSuite) TestRun() { + testCases := []struct { + Name string + Event v1protocol.MembershipUpdateEvent + Expected string + From string + }{ + { + Name: "chat created event", + Event: v1protocol.NewChatCreatedEvent("chat-name", 12), + From: "admin", + Expected: "@admin created the group chat-name", + }, + + { + Name: "chat name changed event", + Event: v1protocol.NewNameChangedEvent("chat-name-2", 12), + From: "admin", + Expected: "@admin changed the group's name to chat-name-2", + }, + { + Name: "members added event", + Event: v1protocol.NewMembersAddedEvent([]string{"a", "b", "c"}, 12), + From: "admin", + Expected: "@admin has invited @a, @b, @c", + }, + { + Name: "member joined event", + Event: v1protocol.NewMemberJoinedEvent(12), + From: "admin", + Expected: "@admin joined the group", + }, + { + Name: "admins added event", + Event: v1protocol.NewAdminsAddedEvent([]string{"a", "b", "c"}, 12), + From: "admin", + Expected: "@admin has made @a, @b, @c admin", + }, + { + Name: "member removed event", + Event: v1protocol.NewMemberRemovedEvent("a", 12), + From: "admin", + Expected: "@a left the group", + }, + { + Name: "admin removed event", + Event: v1protocol.NewAdminRemovedEvent("a", 12), + From: "admin", + Expected: "@a is not admin anymore", + }, + } + for _, tc := range testCases { + s.Run(tc.Name, func() { + tc.Event.From = tc.From + + systemMessage := eventToSystemMessage(tc.Event, defaultSystemMessagesTranslations) + s.Equal(systemMessage.Text, tc.Expected) + + }) + } + +} diff --git a/protocol/message_processor.go b/protocol/message_processor.go index 04850332c..051708fef 100644 --- a/protocol/message_processor.go +++ b/protocol/message_processor.go @@ -14,6 +14,7 @@ import ( datasyncpeer "github.com/status-im/status-go/protocol/datasync/peer" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/encryption/multidevice" + "github.com/status-im/status-go/protocol/protobuf" transport "github.com/status-im/status-go/protocol/transport/whisper" v1protocol "github.com/status-im/status-go/protocol/v1" datasyncnode "github.com/vacp2p/mvds/node" @@ -28,16 +29,11 @@ const ( whisperPoWTime = 5 ) -type messageHandler interface { - HandleMembershipUpdate(m v1protocol.MembershipUpdateMessage) error -} - type messageProcessor struct { identity *ecdsa.PrivateKey datasync *datasync.DataSync protocol *encryption.Protocol transport *transport.WhisperServiceTransport - handler messageHandler logger *zap.Logger featureFlags featureFlags @@ -48,7 +44,6 @@ func newMessageProcessor( database *sql.DB, enc *encryption.Protocol, transport *transport.WhisperServiceTransport, - handler messageHandler, logger *zap.Logger, features featureFlags, ) (*messageProcessor, error) { @@ -71,7 +66,6 @@ func newMessageProcessor( datasync: ds, protocol: enc, transport: transport, - handler: handler, logger: logger, featureFlags: features, } @@ -97,13 +91,14 @@ func (p *messageProcessor) SendPrivateRaw( ctx context.Context, recipient *ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug( "sending a private message", zap.Binary("public-key", crypto.FromECDSAPub(recipient)), zap.String("site", "SendPrivateRaw"), ) - return p.sendPrivate(ctx, recipient, data) + return p.sendPrivate(ctx, recipient, data, messageType) } // SendGroupRaw takes encoded data, encrypts it and sends through the wire, @@ -112,13 +107,14 @@ func (p *messageProcessor) SendGroupRaw( ctx context.Context, recipients []*ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug( "sending a private group message", zap.String("site", "SendGroupRaw"), ) // Calculate messageID first - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -126,7 +122,7 @@ func (p *messageProcessor) SendGroupRaw( messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) for _, recipient := range recipients { - _, err = p.sendPrivate(ctx, recipient, data) + _, err = p.sendPrivate(ctx, recipient, data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to send message") } @@ -139,10 +135,11 @@ func (p *messageProcessor) sendPrivate( ctx context.Context, recipient *ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient))) - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -176,37 +173,34 @@ func (p *messageProcessor) sendPrivate( func (p *messageProcessor) SendMembershipUpdate( ctx context.Context, recipients []*ecdsa.PublicKey, - chatID string, - updates []v1protocol.MembershipUpdate, - clock int64, -) ([][]byte, error) { + group *v1protocol.Group, + chatMessage *protobuf.ChatMessage, +) ([]byte, error) { p.logger.Debug("sending a membership update", zap.Int("membersCount", len(recipients))) message := v1protocol.MembershipUpdateMessage{ - ChatID: chatID, - Updates: updates, + ChatID: group.ChatID(), + Events: group.Events(), + Message: chatMessage, } encodedMessage, err := v1protocol.EncodeMembershipUpdateMessage(message) if err != nil { return nil, errors.Wrap(err, "failed to encode membership update message") } - var resultIDs [][]byte - for _, recipient := range recipients { - messageID, err := p.sendPrivate(ctx, recipient, encodedMessage) - if err != nil { - return nil, err - } - resultIDs = append(resultIDs, messageID) - } - return resultIDs, nil + return p.SendGroupRaw(ctx, recipients, encodedMessage, protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE) } // SendPublicRaw takes encoded data, encrypts it and sends through the wire. -func (p *messageProcessor) SendPublicRaw(ctx context.Context, chatName string, data []byte) ([]byte, error) { +func (p *messageProcessor) SendPublicRaw( + ctx context.Context, + chatName string, + data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, +) ([]byte, error) { var newMessage *types.NewMessage - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -230,16 +224,6 @@ func (p *messageProcessor) SendPublicRaw(ctx context.Context, chatName string, d return messageID, nil } -func (p *messageProcessor) processMembershipUpdate(m v1protocol.MembershipUpdateMessage) error { - if err := m.Verify(); err != nil { - return err - } - if p.handler != nil { - return p.handler.HandleMembershipUpdate(m) - } - return errors.New("missing handler") -} - func (p *messageProcessor) processPairMessage(m v1protocol.PairMessage) error { metadata := &multidevice.InstallationMetadata{ Name: m.Name, @@ -336,8 +320,8 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe return nil } -func (p *messageProcessor) wrapMessageV1(encodedMessage []byte) ([]byte, error) { - wrappedMessage, err := v1protocol.WrapMessageV1(encodedMessage, p.identity) +func (p *messageProcessor) wrapMessageV1(encodedMessage []byte, messageType protobuf.ApplicationMetadataMessage_Type) ([]byte, error) { + wrappedMessage, err := v1protocol.WrapMessageV1(encodedMessage, messageType, p.identity) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } diff --git a/protocol/message_processor_test.go b/protocol/message_processor_test.go index 2012af0f8..098280cdf 100644 --- a/protocol/message_processor_test.go +++ b/protocol/message_processor_test.go @@ -98,7 +98,6 @@ func (s *MessageProcessorSuite) SetupTest() { database, encryptionProtocol, whisperTransport, - nil, s.logger, featureFlags{}, ) @@ -120,7 +119,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() { encodedPayload, err := proto.Marshal(&s.testMessage) s.Require().NoError(err) - wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, authorKey) + wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, authorKey) s.Require().NoError(err) message := &types.Message{} @@ -136,7 +135,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() { parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) - s.Require().Equal(v1protocol.MessageT, decodedMessages[0].MessageType) + s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { @@ -149,7 +148,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { encodedPayload, err := proto.Marshal(&s.testMessage) s.Require().NoError(err) - wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, authorKey) + wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, authorKey) s.Require().NoError(err) dataSyncMessage := datasyncproto.Payload{ @@ -173,7 +172,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) - s.Require().Equal(v1protocol.MessageT, decodedMessages[0].MessageType) + s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { @@ -186,7 +185,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { encodedPayload, err := proto.Marshal(&s.testMessage) s.Require().NoError(err) - wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, authorKey) + wrappedPayload, err := v1protocol.WrapMessageV1(encodedPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, authorKey) s.Require().NoError(err) dataSyncMessage := datasyncproto.Payload{ @@ -234,5 +233,5 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) - s.Require().Equal(v1protocol.MessageT, decodedMessages[0].MessageType) + s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } diff --git a/protocol/message_validator.go b/protocol/message_validator.go new file mode 100644 index 000000000..7c1bd5623 --- /dev/null +++ b/protocol/message_validator.go @@ -0,0 +1,51 @@ +package protocol + +import ( + "errors" + "github.com/status-im/status-go/protocol/protobuf" + "strings" +) + +func ValidateReceivedChatMessage(message *protobuf.ChatMessage) error { + if message.Clock == 0 { + return errors.New("Clock can't be 0") + } + + if message.Timestamp == 0 { + return errors.New("Timestamp can't be 0") + } + + if len(strings.TrimSpace(message.Text)) == 0 { + return errors.New("Text can't be empty") + } + + if len(message.ChatId) == 0 { + return errors.New("ChatId can't be empty") + } + + if message.ContentType == protobuf.ChatMessage_UNKNOWN_CONTENT_TYPE { + return errors.New("Unknown content type") + } + + if message.MessageType == protobuf.ChatMessage_UNKNOWN_MESSAGE_TYPE || message.MessageType == protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP { + return errors.New("Unknown message type") + } + + if message.ContentType == protobuf.ChatMessage_STICKER { + if message.Payload == nil { + return errors.New("No sticker content") + } + sticker := message.GetSticker() + if sticker == nil { + return errors.New("No sticker content") + } + + if sticker.Pack == 0 { + return errors.New("Sticker pack not set") + } + if len(sticker.Hash) == 0 { + return errors.New("Sticker hash not set") + } + } + return nil +} diff --git a/protocol/message_validator_test.go b/protocol/message_validator_test.go new file mode 100644 index 000000000..d91c7b4b5 --- /dev/null +++ b/protocol/message_validator_test.go @@ -0,0 +1,260 @@ +package protocol + +import ( + "testing" + + "github.com/status-im/status-go/protocol/protobuf" + "github.com/stretchr/testify/suite" +) + +type MessageValidatorSuite struct { + suite.Suite +} + +func TestMessageValidatorSuite(t *testing.T) { + suite.Run(t, new(MessageValidatorSuite)) +} + +func (s *MessageValidatorSuite) TestValidatePlainTextMessage() { + testCases := []struct { + Name string + Valid bool + Message protobuf.ChatMessage + }{ + { + Name: "A valid message", + Valid: true, + Message: protobuf.ChatMessage{ + ChatId: "a", + Clock: 1, + Timestamp: 2, + Text: "some-text", + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Missing chatId", + Valid: false, + Message: protobuf.ChatMessage{ + Clock: 1, + Timestamp: 2, + Text: "some-text", + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Missing clock", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Timestamp: 2, + Text: "some-text", + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Missing timestamp", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Clock: 2, + Text: "some-text", + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Missing text", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Blank text", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: " \n \t \n ", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Unknown MessageType", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_UNKNOWN_MESSAGE_TYPE, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Unknown ContentType", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_UNKNOWN_CONTENT_TYPE, + }, + }, + { + Name: "System message MessageType", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + }, + }, + { + Name: "Valid emoji only emssage", + Valid: true, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: ":+1:", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_EMOJI, + }, + }, + // TODO: FIX ME + /* { + Name: "Invalid emoji only emssage", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: ":+1: not valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_EMOJI, + }, + } + ,*/ + { + Name: "Valid sticker message", + Valid: true, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + Payload: &protobuf.ChatMessage_Sticker{ + Sticker: &protobuf.StickerMessage{ + Pack: 1, + Hash: "some-hash", + }, + }, + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_STICKER, + }, + }, + { + Name: "Invalid sticker message without Pack", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + Payload: &protobuf.ChatMessage_Sticker{ + Sticker: &protobuf.StickerMessage{ + Hash: "some-hash", + }, + }, + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_STICKER, + }, + }, + { + Name: "Invalid sticker message without Hash", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + Payload: &protobuf.ChatMessage_Sticker{ + Sticker: &protobuf.StickerMessage{ + Pack: 1, + }, + }, + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_STICKER, + }, + }, + { + Name: "Invalid sticker message without any content", + Valid: false, + Message: protobuf.ChatMessage{ + ChatId: "a", + Text: "valid", + Clock: 2, + Timestamp: 3, + ResponseTo: "", + EnsName: "", + MessageType: protobuf.ChatMessage_ONE_TO_ONE, + ContentType: protobuf.ChatMessage_STICKER, + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.Name, func() { + err := ValidateReceivedChatMessage(&tc.Message) + if tc.Valid { + s.Nil(err) + } else { + s.NotNil(err) + } + }) + } +} diff --git a/protocol/messenger.go b/protocol/messenger.go index 594b122ba..b0cf326d5 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -6,7 +6,8 @@ import ( "database/sql" "encoding/hex" "encoding/json" - "strconv" + "math/rand" + "sync" "time" "github.com/pkg/errors" @@ -44,17 +45,20 @@ var ( // Similarly, it needs to expose an interface to manage // mailservers because they can also be managed by the user. type Messenger struct { - node types.Node - identity *ecdsa.PrivateKey - persistence *sqlitePersistence - transport *transport.WhisperServiceTransport - encryptor *encryption.Protocol - processor *messageProcessor - logger *zap.Logger - + node types.Node + identity *ecdsa.PrivateKey + persistence *sqlitePersistence + transport *transport.WhisperServiceTransport + encryptor *encryption.Protocol + processor *messageProcessor + logger *zap.Logger featureFlags featureFlags messagesPersistenceEnabled bool shutdownTasks []func() error + systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string + allChats map[string]*Chat + allContacts map[string]*Contact + mutex sync.Mutex } type RawResponse struct { @@ -95,6 +99,8 @@ type config struct { // DEPRECATED: no need to expose it onSendContactCodeHandler func(*encryption.ProtocolMessageSpec) + // systemMessagesTranslations holds translations for system-messages + systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string // Config for the envelopes monitor envelopesMonitorConfig *transport.EnvelopesMonitorConfig @@ -118,6 +124,13 @@ func WithOnNewInstallationsHandler(h func([]*multidevice.Installation)) Option { } } +func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { + return func(c *config) error { + c.systemMessagesTranslations = t + return nil + } +} + func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option { return func(c *config) error { c.onNegotiatedFilters = h @@ -222,7 +235,7 @@ func NewMessenger( if c.onSendContactCodeHandler == nil { c.onSendContactCodeHandler = func(messageSpec *encryption.ProtocolMessageSpec) { slogger := logger.With(zap.String("site", "onSendContactCodeHandler")) - slogger.Info("received a SendContactCode request") + slogger.Debug("received a SendContactCode request") newMessage, err := messageSpecToWhisper(messageSpec) if err != nil { @@ -240,6 +253,10 @@ func NewMessenger( } } + if c.systemMessagesTranslations == nil { + c.systemMessagesTranslations = defaultSystemMessagesTranslations + } + // Configure the database. database := c.db if c.db == nil && c.dbConfig == (dbConfig{}) { @@ -288,7 +305,6 @@ func NewMessenger( database, encryptionProtocol, t, - newPersistentMessageHandler(&sqlitePersistence{db: database}), logger, c.featureFlags, ) @@ -304,6 +320,9 @@ func NewMessenger( encryptor: encryptionProtocol, processor: processor, featureFlags: c.featureFlags, + systemMessagesTranslations: c.systemMessagesTranslations, + allChats: make(map[string]*Chat), + allContacts: make(map[string]*Contact), messagesPersistenceEnabled: c.messagesPersistenceEnabled, shutdownTasks: []func() error{ database.Close, @@ -331,6 +350,12 @@ func NewMessenger( // Init analyzes chats and contacts in order to setup filters // which are responsible for retrieving messages. func (m *Messenger) Init() error { + m.mutex.Lock() + defer m.mutex.Unlock() + + // Seed the for color generation + rand.Seed(time.Now().Unix()) + logger := m.logger.With(zap.String("site", "Init")) var ( @@ -340,11 +365,12 @@ func (m *Messenger) Init() error { // Get chat IDs and public keys from the existing chats. // TODO: Get only active chats by the query. - chats, err := m.Chats() + chats, err := m.persistence.Chats() if err != nil { return err } for _, chat := range chats { + m.allChats[chat.ID] = chat if !chat.Active { continue } @@ -352,7 +378,11 @@ func (m *Messenger) Init() error { case ChatTypePublic: publicChatIDs = append(publicChatIDs, chat.ID) case ChatTypeOneToOne: - publicKeys = append(publicKeys, chat.PublicKey) + pk, err := chat.PublicKey() + if err != nil { + return err + } + publicKeys = append(publicKeys, pk) case ChatTypePrivateGroupChat: for _, member := range chat.Members { publicKey, err := member.PublicKey() @@ -367,11 +397,12 @@ func (m *Messenger) Init() error { } // Get chat IDs and public keys from the contacts. - contacts, err := m.Contacts() + contacts, err := m.persistence.Contacts() if err != nil { return err } for _, contact := range contacts { + m.allContacts[contact.ID] = contact // We only need filters for contacts added by us and not blocked. if !contact.IsAdded() || contact.IsBlocked() { continue @@ -462,7 +493,12 @@ func (m *Messenger) Mailservers() ([]string, error) { func (m *Messenger) Join(chat Chat) error { switch chat.ChatType { case ChatTypeOneToOne: - return m.transport.JoinPrivate(chat.PublicKey) + pk, err := chat.PublicKey() + if err != nil { + return err + } + + return m.transport.JoinPrivate(pk) case ChatTypePrivateGroupChat: members, err := chat.MembersAsPublicKeys() if err != nil { @@ -476,129 +512,399 @@ func (m *Messenger) Join(chat Chat) error { } } +// This is not accurate, it should not leave transport on removal of chat/group +// only once there is no more: Group chat with that member, one-to-one chat, contact added by us func (m *Messenger) Leave(chat Chat) error { - if chat.PublicKey != nil { - return m.transport.LeavePrivate(chat.PublicKey) - } else if chat.Name != "" { + switch chat.ChatType { + case ChatTypeOneToOne: + pk, err := chat.PublicKey() + if err != nil { + return err + } + return m.transport.LeavePrivate(pk) + case ChatTypePrivateGroupChat: + members, err := chat.MembersAsPublicKeys() + if err != nil { + return err + } + return m.transport.LeaveGroup(members) + case ChatTypePublic: return m.transport.LeavePublic(chat.Name) + default: + return errors.New("chat is neither public nor private") } - return errors.New("chat is neither public nor private") } -// TODO: consider moving to a ChatManager ??? -func (m *Messenger) CreateGroupChat(name string) (*Chat, error) { +func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers")) + logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members)) chat := createGroupChat() group, err := v1protocol.NewGroupWithCreator(name, m.identity) if err != nil { return nil, err } chat.updateChatFromProtocolGroup(group) - return &chat, nil -} -func (m *Messenger) AddMembersToChat(ctx context.Context, chat *Chat, members []*ecdsa.PublicKey) error { - group, err := newProtocolGroupFromChat(chat) - if err != nil { - return err - } - encodedMembers := make([]string, len(members)) - for idx, member := range members { - encodedMembers[idx] = types.EncodeHex(crypto.FromECDSAPub(member)) - } - event := v1protocol.NewMembersAddedEvent(encodedMembers, group.NextClockValue()) - err = group.ProcessEvent(&m.identity.PublicKey, event) - if err != nil { - return err - } - if err := m.propagateMembershipUpdates(ctx, group); err != nil { - return err - } - chat.updateChatFromProtocolGroup(group) - return m.SaveChat(*chat) -} - -func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chat *Chat) error { - group, err := newProtocolGroupFromChat(chat) - if err != nil { - return err - } - event := v1protocol.NewMemberJoinedEvent( - types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), - group.NextClockValue(), - ) - err = group.ProcessEvent(&m.identity.PublicKey, event) - if err != nil { - return err - } - if err := m.propagateMembershipUpdates(ctx, group); err != nil { - return err - } - chat.updateChatFromProtocolGroup(group) - return m.SaveChat(*chat) -} - -func (m *Messenger) propagateMembershipUpdates(ctx context.Context, group *v1protocol.Group) error { - events := make([]v1protocol.MembershipUpdateEvent, len(group.Updates())) - for idx, event := range group.Updates() { - events[idx] = event.MembershipUpdateEvent - } - update := v1protocol.MembershipUpdate{ - ChatID: group.ChatID(), - Events: events, - } - if err := update.Sign(m.identity); err != nil { - return err - } - recipients, err := stringSliceToPublicKeys(group.Members(), true) - if err != nil { - return err - } - // Filter out my key from the recipients - n := 0 - for _, recipient := range recipients { - if !isPubKeyEqual(recipient, &m.identity.PublicKey) { - recipients[n] = recipient - n++ - } - } - recipients = recipients[:n] - // Finally send membership updates to all recipients. - _, err = m.processor.SendMembershipUpdate( - ctx, - recipients, - group.ChatID(), - []v1protocol.MembershipUpdate{update}, - group.NextClockValue(), - ) - return err -} - -func (m *Messenger) SaveChat(chat Chat) error { - return m.persistence.SaveChat(chat) -} - -func (m *Messenger) Chats() ([]*Chat, error) { - return m.persistence.Chats() -} - -func (m *Messenger) DeleteChat(chatID string) error { - return m.persistence.DeleteChat(chatID) -} - -func (m *Messenger) chatByID(id string) (*Chat, error) { - chats, err := m.persistence.Chats() + // Add members + event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) if err != nil { return nil, err } - for _, c := range chats { - if c.ID == id { - return c, nil - } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err } - return nil, errChatNotFound + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{&chat} + response.Messages = buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations) + return &response, m.saveChat(&chat) } -func (m *Messenger) SaveContact(contact Contact) error { +func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string, member string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "RemoveMemberFromGroupChat")) + logger.Info("Removing member form group chat", zap.String("chatID", chatID), zap.String("member", member)) + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // We save the initial recipients as we want to send updates to also + // the members kicked out + oldRecipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + // Remove member + event := v1protocol.NewMemberRemovedEvent(member, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + if _, err := m.propagateMembershipUpdates(ctx, group, oldRecipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations) + return &response, m.saveChat(chat) +} + +func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "AddMembersFromGroupChat")) + logger.Info("Adding members form group chat", zap.String("chatID", chatID), zap.Any("members", members)) + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // Add members + event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "AddAdminsToGroupChat")) + logger.Info("Add admins to group chat", zap.String("chatID", chatID), zap.Any("members", members)) + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // Add members + event := v1protocol.NewAdminsAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + err := m.Join(*chat) + if err != nil { + return nil, err + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + event := v1protocol.NewMemberJoinedEvent( + group.NextClockValue(), + ) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + err := m.Leave(*chat) + if err != nil { + return nil, err + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + event := v1protocol.NewMemberRemovedEvent( + types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + group.NextClockValue(), + ) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + chat.Active = false + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + return &response, m.saveChat(chat) +} + +func (m *Messenger) propagateMembershipUpdates(ctx context.Context, group *v1protocol.Group, recipients []*ecdsa.PublicKey, chatMessage *protobuf.ChatMessage) ([]byte, error) { + hasPairedDevices, err := m.hasPairedDevices() + if err != nil { + return nil, err + } + + if !hasPairedDevices { + // Filter out my key from the recipients + n := 0 + for _, recipient := range recipients { + if !isPubKeyEqual(recipient, &m.identity.PublicKey) { + recipients[n] = recipient + n++ + } + } + recipients = recipients[:n] + } + // Finally send membership updates to all recipients. + return m.processor.SendMembershipUpdate( + ctx, + recipients, + group, + chatMessage, + ) +} + +func (m *Messenger) saveChat(chat *Chat) error { + err := m.persistence.SaveChat(*chat) + if err != nil { + return err + } + m.allChats[chat.ID] = chat + + return nil +} + +func (m *Messenger) saveChats(chats []*Chat) error { + err := m.persistence.SaveChats(chats) + if err != nil { + return err + } + for _, chat := range chats { + m.allChats[chat.ID] = chat + } + + return nil + +} + +func (m *Messenger) SaveChat(chat *Chat) error { + m.mutex.Lock() + defer m.mutex.Unlock() + err := m.saveChat(chat) + if err != nil { + return err + } + + return nil +} + +func (m *Messenger) Chats() []*Chat { + m.mutex.Lock() + defer m.mutex.Unlock() + + var chats []*Chat + + for _, c := range m.allChats { + chats = append(chats, c) + } + + return chats +} + +func (m *Messenger) DeleteChat(chatID string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + err := m.persistence.DeleteChat(chatID) + if err != nil { + return err + } + delete(m.allChats, chatID) + + return nil +} + +func (m *Messenger) SaveContact(contact *Contact) error { + m.mutex.Lock() + defer m.mutex.Unlock() + identicon, err := identicon.GenerateBase64(contact.ID) if err != nil { return err @@ -613,15 +919,39 @@ func (m *Messenger) SaveContact(contact Contact) error { contact.Alias = name - return m.persistence.SaveContact(contact, nil) + err = m.persistence.SaveContact(contact, nil) + if err != nil { + return err + } + + m.allContacts[contact.ID] = contact + return nil } -func (m *Messenger) BlockContact(contact Contact) ([]*Chat, error) { - return m.persistence.BlockContact(contact) +func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + chats, err := m.persistence.BlockContact(contact) + if err != nil { + return nil, err + } + m.allContacts[contact.ID] = contact + for _, chat := range chats { + m.allChats[chat.ID] = chat + } + delete(m.allChats, contact.ID) + return chats, nil } -func (m *Messenger) Contacts() ([]*Contact, error) { - return m.persistence.Contacts() +func (m *Messenger) Contacts() []*Contact { + m.mutex.Lock() + defer m.mutex.Unlock() + var contacts []*Contact + for _, contact := range m.allContacts { + contacts = append(contacts, contact) + } + + return contacts } func timestampInMs() uint64 { @@ -630,6 +960,9 @@ func timestampInMs() uint64 { // ReSendChatMessage pulls a message from the database and sends it again func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + logger := m.logger.With(zap.String("site", "ReSendChatMessage")) var response MessengerResponse message, err := m.persistence.MessageByID(messageID) @@ -643,28 +976,33 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M return nil, errors.New("message payload not found, can't resend message") } - chat, err := m.chatByID(message.LocalChatID) - if err != nil { - return nil, err + chat, ok := m.allChats[message.LocalChatID] + if !ok { + return nil, errors.New("chat not found") } switch chat.ChatType { case ChatTypeOneToOne: - publicKey := crypto.FromECDSAPub(chat.PublicKey) - logger.Debug("re-sending private message", zap.Binary("publicKey", publicKey)) - id, err := m.processor.SendPrivateRaw(ctx, chat.PublicKey, message.RawPayload) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + logger.Debug("re-sending private message") + id, err := m.processor.SendPrivateRaw(ctx, publicKey, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, message.RawPayload) + err = m.sendToPairedDevices(ctx, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } case ChatTypePublic: logger.Debug("re-sending public message", zap.String("chatName", chat.Name)) - id, err := m.processor.SendPublicRaw(ctx, chat.ID, message.RawPayload) + id, err := m.processor.SendPublicRaw(ctx, chat.ID, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -683,14 +1021,16 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M n++ } } - id, err := m.processor.SendGroupRaw(ctx, recipients[:n], message.RawPayload) + id, err := m.processor.SendGroupRaw(ctx, recipients[:n], message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, message.RawPayload) + err = m.sendToPairedDevices(ctx, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -704,15 +1044,23 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M return &response, nil } -// sendToPairedDevices will check if we have any paired devices and send to them if necessary -func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte) error { +func (m *Messenger) hasPairedDevices() (bool, error) { activeInstallations, err := m.encryptor.GetOurActiveInstallations(&m.identity.PublicKey) + if err != nil { + return false, err + } + return len(activeInstallations) > 1, nil +} + +// sendToPairedDevices will check if we have any paired devices and send to them if necessary +func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte, messageType protobuf.ApplicationMetadataMessage_Type) error { + hasPairedDevices, err := m.hasPairedDevices() if err != nil { return err } // We send a message to any paired device - if len(activeInstallations) > 1 { - _, err := m.processor.SendPrivateRaw(ctx, &m.identity.PublicKey, payload) + if hasPairedDevices { + _, err := m.processor.SendPrivateRaw(ctx, &m.identity.PublicKey, payload, messageType) if err != nil { return err } @@ -722,13 +1070,16 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte) err // SendChatMessage takes a minimal message and sends it based on the corresponding chat func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + logger := m.logger.With(zap.String("site", "Send"), zap.String("chatID", message.ChatId)) var response MessengerResponse // A valid added chat is required. - chat, err := m.chatByID(message.ChatId) - if err != nil { - return nil, err + chat, ok := m.allChats[message.ChatId] + if !ok { + return nil, errors.New("Chat not found") } clock := chat.LastClockValue @@ -764,8 +1115,11 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes switch chat.ChatType { case ChatTypeOneToOne: - publicKey := crypto.FromECDSAPub(chat.PublicKey) - logger.Debug("sending private message", zap.Binary("publicKey", publicKey)) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + logger.Debug("sending private message") message.MessageType = protobuf.ChatMessage_ONE_TO_ONE encodedMessage, err := proto.Marshal(message) if err != nil { @@ -773,13 +1127,15 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } message.RawPayload = encodedMessage - id, err := m.processor.SendPrivateRaw(ctx, chat.PublicKey, encodedMessage) + id, err := m.processor.SendPrivateRaw(ctx, publicKey, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, encodedMessage) + err = m.sendToPairedDevices(ctx, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -793,7 +1149,8 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } message.RawPayload = encodedMessage - id, err := m.processor.SendPublicRaw(ctx, chat.ID, encodedMessage) + id, err := m.processor.SendPublicRaw(ctx, chat.ID, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -801,37 +1158,24 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes case ChatTypePrivateGroupChat: logger.Debug("sending public message", zap.String("chatName", chat.Name)) message.MessageType = protobuf.ChatMessage_PRIVATE_GROUP - encodedMessage, err := proto.Marshal(message) - if err != nil { - return nil, err - } - message.RawPayload = encodedMessage - logger.Debug("sending group message", zap.String("chatName", chat.Name)) recipients, err := chat.MembersAsPublicKeys() if err != nil { return nil, err } - n := 0 - for _, item := range recipients { - if !isPubKeyEqual(item, &m.identity.PublicKey) { - recipients[n] = item - n++ - } - } - id, err := m.processor.SendGroupRaw(ctx, recipients[:n], encodedMessage) + group, err := newProtocolGroupFromChat(chat) if err != nil { return nil, err } + encodedMessage, err := m.propagateMembershipUpdates(ctx, group, recipients, &message.ChatMessage) + if err != nil { + return nil, err + } + + id := v1protocol.MessageID(&m.identity.PublicKey, encodedMessage) message.ID = "0x" + hex.EncodeToString(id) - - err = m.sendToPairedDevices(ctx, encodedMessage) - if err != nil { - return nil, err - } - default: return nil, errors.New("chat type not supported") } @@ -849,9 +1193,6 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes chat.LastClockValue = clock chat.LastMessage = jsonMessage chat.Timestamp = int64(timestamp) - if err := m.SaveChat(*chat); err != nil { - return nil, err - } err = m.persistence.SaveMessagesLegacy([]*Message{message}) if err != nil { @@ -860,16 +1201,20 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes response.Chats = []*Chat{chat} response.Messages = []*Message{message} - return &response, nil + return &response, m.saveChat(chat) } // SendRaw takes encoded data, encrypts it and sends through the wire. // DEPRECATED func (m *Messenger) SendRaw(ctx context.Context, chat Chat, data []byte) ([]byte, error) { - if chat.PublicKey != nil { - return m.processor.SendPrivateRaw(ctx, chat.PublicKey, data) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + if publicKey != nil { + return m.processor.SendPrivateRaw(ctx, publicKey, data, protobuf.ApplicationMetadataMessage_UNKNOWN) } else if chat.Name != "" { - return m.processor.SendPublicRaw(ctx, chat.Name, data) + return m.processor.SendPublicRaw(ctx, chat.Name, data, protobuf.ApplicationMetadataMessage_UNKNOWN) } return nil, errors.New("chat is neither public nor private") } @@ -885,36 +1230,121 @@ func (m *Messenger) RetrieveAll() (*MessengerResponse, error) { return m.handleRetrievedMessages(chatWithMessages) } +type ReceivedMessageState struct { + // Message is the protobuf message received + Message protobuf.ChatMessage + // MessageID is the ID of the message + MessageID string + // WhisperTimestamp is the whisper timestamp of the message + WhisperTimestamp uint64 + // Contact is the contact associated with the author of the message + Contact *Contact + // PublicKey is the public key of the author of the message + PublicKey *ecdsa.PublicKey + // List of chats modified + ModifiedChats map[string]bool + PostProcessor *postProcessor +} + +func (m *Messenger) handleChatMessage(state *ReceivedMessageState) (*Message, error) { + logger := m.logger.With(zap.String("site", "handleChatMessage")) + if err := ValidateReceivedChatMessage(&state.Message); err != nil { + logger.Warn("failed to validate message", zap.Error(err)) + return nil, err + } + receivedMessage := &Message{ + ID: state.MessageID, + ChatMessage: state.Message, + From: state.Contact.ID, + Alias: state.Contact.Alias, + SigPubKey: state.PublicKey, + Identicon: state.Contact.Identicon, + WhisperTimestamp: state.WhisperTimestamp, + } + receivedMessage.PrepareContent() + chat, err := state.PostProcessor.matchMessage(receivedMessage, m.allChats) + if err != nil { + return nil, err + } + + // If deleted-at is greater, ignore message + if chat.DeletedAtClockValue >= receivedMessage.Clock { + return nil, nil + } + + // Set the LocalChatID for the message + receivedMessage.LocalChatID = chat.ID + + if c, ok := m.allChats[chat.ID]; ok { + chat = c + } + + // Set the LocalChatID for the message + receivedMessage.LocalChatID = chat.ID + + // Increase unviewed count + if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { + chat.UnviewedMessagesCount++ + } else { + // Our own message, mark as sent + receivedMessage.OutgoingStatus = OutgoingStatusSent + } + + // Update chat timestamp + chat.Timestamp = int64(timestampInMs()) + // Update last clock value + if chat.LastClockValue <= receivedMessage.Clock { + chat.LastClockValue = receivedMessage.Clock + encodedLastMessage, err := json.Marshal(receivedMessage) + if err != nil { + return nil, err + } + chat.LastMessage = encodedLastMessage + } + + // Set chat active + chat.Active = true + // Set in the modified maps chat + state.ModifiedChats[chat.ID] = true + m.allChats[chat.ID] = chat + + return receivedMessage, nil +} + +func (m *Messenger) messageExists(messageID string, existingMessagesMap map[string]bool) (bool, error) { + if _, ok := existingMessagesMap[messageID]; ok { + return true, nil + } + + existingMessagesMap[messageID] = true + + // Check against the database, this is probably a bit slow for + // each message, but for now might do, we'll make it faster later + existingMessage, err := m.persistence.MessageByID(messageID) + if err != nil && err != errRecordNotFound { + return false, err + } + if existingMessage != nil { + return true, nil + } + return false, nil +} + func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() response := &MessengerResponse{ Chats: []*Chat{}, Messages: []*Message{}, } - allChats, err := m.persistence.Chats() - if err != nil { - return nil, err - } postProcessor := newPostProcessor(m, postProcessorConfig{MatchChat: true}) logger := m.logger.With(zap.String("site", "RetrieveAll")) rawMessages := make(map[transport.Filter][]*v1protocol.StatusMessage) - // We should query this instead - contacts, err := m.Contacts() - if err != nil { - return nil, err - } - - blockedContacts := make(map[string]bool) - for _, c := range contacts { - if c.IsBlocked() { - blockedContacts[c.ID] = true - } - } - - allContactsMap := make(map[string]*Contact) - allChatsMap := make(map[string]*Chat) + modifiedChats := make(map[string]bool) + modifiedContacts := make(map[string]bool) existingMessagesMap := make(map[string]bool) for chat, messages := range chatWithMessages { @@ -927,35 +1357,25 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } for _, msg := range statusMessages { + publicKey := msg.SigPubKey() + // Check for messages from blocked users - senderID := "0x" + hex.EncodeToString(crypto.FromECDSAPub(msg.SigPubKey())) - if blockedContacts[senderID] { + senderID := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + if _, ok := m.allContacts[senderID]; ok && m.allContacts[senderID].IsBlocked() { continue } // Don't process duplicates - messageID := "0x" + hex.EncodeToString(msg.ID) - if _, ok := existingMessagesMap[messageID]; ok { + messageID := types.EncodeHex(msg.ID) + exists, err := m.messageExists(messageID, existingMessagesMap) + if err != nil { + logger.Warn("failed to check message exists", zap.Error(err)) + } + if exists { continue } - existingMessagesMap[messageID] = true - - // Check against the database, this is probably a bit slow for - // each message, but for now might do, we'll make it faster later - existingMessage, err := m.persistence.MessageByID(messageID) - if err != nil && err != errRecordNotFound { - return nil, err - } - if existingMessage != nil { - continue - } - - publicKey := msg.SigPubKey() - if publicKey == nil { - return nil, errors.New("public key can't be nil") - } var contact *Contact - if c, ok := allContactsMap[senderID]; ok { + if c, ok := m.allContacts[senderID]; ok { contact = c } else { c, err := buildContact(publicKey) @@ -964,88 +1384,118 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } contact = c - allContactsMap[senderID] = c - response.Contacts = append(response.Contacts, c) + m.allContacts[senderID] = c + modifiedContacts[contact.ID] = true + } + messageState := &ReceivedMessageState{ + MessageID: messageID, + WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000, + Contact: contact, + PublicKey: publicKey, + ModifiedChats: modifiedChats, + PostProcessor: postProcessor, } if msg.ParsedMessage != nil { - if textMessage, ok := msg.ParsedMessage.(protobuf.ChatMessage); ok { - receivedMessage := &Message{ - ID: messageID, - ChatMessage: textMessage, - From: contact.ID, - Alias: contact.Alias, - SigPubKey: publicKey, - Identicon: contact.Identicon, - WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000, - } - receivedMessage.PrepareContent() + switch msg.ParsedMessage.(type) { + case protobuf.MembershipUpdateMessage: - chat, err := postProcessor.matchMessage(receivedMessage, allChats) + rawMembershipUpdate := msg.ParsedMessage.(protobuf.MembershipUpdateMessage) + membershipUpdate, err := v1protocol.MembershipUpdateMessageFromProtobuf(&rawMembershipUpdate) if err != nil { - logger.Warn("failed to match message", zap.String("receivedChatID", receivedMessage.ChatId), zap.Error(err)) + logger.Warn("failed to process membership update", zap.Error(err)) + continue + + } + + chat, systemMessages, err := HandleMembershipUpdate(m.allChats[membershipUpdate.ChatID], membershipUpdate, types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), m.systemMessagesTranslations) + if err != nil { + logger.Warn("failed to process membership update", zap.Error(err)) continue } - // If deleted-at is greater, ignore message - if chat.DeletedAtClockValue >= receivedMessage.Clock { - continue - } - - // Set the LocalChatID for the message - receivedMessage.LocalChatID = chat.ID - - if c, ok := allChatsMap[chat.ID]; ok { - chat = c - } - - // Increase unviewed count - if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { - chat.UnviewedMessagesCount++ - } else { - // Our own message, mark as sent - receivedMessage.OutgoingStatus = OutgoingStatusSent - } - - // Update chat timestamp - chat.Timestamp = int64(timestampInMs()) - // Update last clock value - if chat.LastClockValue <= receivedMessage.Clock { - chat.LastClockValue = receivedMessage.Clock - encodedLastMessage, err := json.Marshal(receivedMessage) + for _, message := range systemMessages { + messageID := message.ID + exists, err := m.messageExists(messageID, existingMessagesMap) if err != nil { - return nil, err + logger.Warn("failed to check message exists", zap.Error(err)) } - chat.LastMessage = encodedLastMessage + if exists { + continue + } + response.Messages = append(response.Messages, message) } - // Set chat active - chat.Active = true + // Store in chats map as it might be a new one + m.allChats[chat.ID] = chat // Set in the map - allChatsMap[chat.ID] = chat + modifiedChats[chat.ID] = true + + if rawMembershipUpdate.Message != nil { + messageState.Message = *rawMembershipUpdate.Message + receivedMessage, err := m.handleChatMessage(messageState) + if err != nil { + logger.Warn("failed to process message", zap.Error(err)) + continue + } + // Add to response + if receivedMessage != nil { + response.Messages = append(response.Messages, receivedMessage) + } + + } + + case protobuf.ChatMessage: + messageState.Message = msg.ParsedMessage.(protobuf.ChatMessage) + receivedMessage, err := m.handleChatMessage(messageState) + if err != nil { + logger.Warn("failed to process message", zap.Error(err)) + continue + } // Add to response - response.Messages = append(response.Messages, receivedMessage) + if receivedMessage != nil { + response.Messages = append(response.Messages, receivedMessage) + } + default: + // RawMessage, not processed here, pass straight to the client + rawMessages[chat] = append(rawMessages[chat], msg) + } } else { - // RawMessage, not processed here, pass straight to the client rawMessages[chat] = append(rawMessages[chat], msg) } - } } } - err = m.persistence.SetContactsGeneratedData(response.Contacts, nil) - if err != nil { - return nil, err + for id, _ := range modifiedChats { + response.Chats = append(response.Chats, m.allChats[id]) } - for _, c := range allChatsMap { - response.Chats = append(response.Chats, c) + for id, _ := range modifiedContacts { + response.Contacts = append(response.Contacts, m.allContacts[id]) } - m.persistence.SaveChats(response.Chats) - m.SaveMessages(response.Messages) + var err error + if len(response.Chats) > 0 { + err = m.saveChats(response.Chats) + if err != nil { + return nil, err + } + } + if len(response.Messages) > 0 { + err = m.SaveMessages(response.Messages) + if err != nil { + return nil, err + } + } + + if len(response.Contacts) > 0 { + err = m.persistence.SaveContacts(response.Contacts) + if err != nil { + return nil, err + } + } for filter, messages := range rawMessages { response.RawMessages = append(response.RawMessages, &RawResponse{Filter: &filter, Messages: messages}) @@ -1102,47 +1552,6 @@ func (m *Messenger) SaveMessages(messages []*Message) error { return m.persistence.SaveMessagesLegacy(messages) } -// AddSystemMessages format an array of system-messages and saves them to the database -// It's needed until group chats are fully in status-go. -func (m *Messenger) AddSystemMessages(messages []*Message) ([]*Message, error) { - timestamp := timestampInMs() - - for _, message := range messages { - message.LocalChatID = message.ChatId - message.Timestamp = timestamp - message.WhisperTimestamp = timestamp - message.Seen = true - - identicon, err := identicon.GenerateBase64(message.From) - if err != nil { - return nil, err - } - - message.Identicon = identicon - - alias, err := alias.GenerateFromPublicKeyString(message.From) - if err != nil { - return nil, err - } - - message.ID = "0x" + hex.EncodeToString(crypto.Keccak256([]byte(message.Text+message.From+strconv.FormatUint(message.Clock, 10)))) - message.Alias = alias - message.ContentType = protobuf.ChatMessage_STATUS - message.MessageType = protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP - err = message.PrepareContent() - if err != nil { - return nil, err - } - } - - err := m.SaveMessages(messages) - if err != nil { - return nil, err - } - - return messages, nil -} - // DEPRECATED: required by status-react. func (m *Messenger) DeleteMessage(id string) error { return m.persistence.DeleteMessage(id) @@ -1155,7 +1564,19 @@ func (m *Messenger) DeleteMessagesByChatID(id string) error { // DEPRECATED: required by status-react. func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) error { - return m.persistence.MarkMessagesSeen(chatID, ids) + m.mutex.Lock() + defer m.mutex.Unlock() + + err := m.persistence.MarkMessagesSeen(chatID, ids) + if err != nil { + return err + } + chat, err := m.persistence.Chat(chatID) + if err != nil { + return err + } + m.allChats[chatID] = chat + return nil } // DEPRECATED: required by status-react. @@ -1189,26 +1610,7 @@ func newPostProcessor(m *Messenger, config postProcessorConfig) *postProcessor { } } -func (p *postProcessor) matchMessages(messages []*Message) ([]*Message, error) { - chats, err := p.persistence.Chats() - if err != nil { - return nil, err - } - - result := make([]*Message, 0, len(messages)) - for _, message := range messages { - chat, err := p.matchMessage(message, chats) - if err != nil { - p.logger.Error("failed to match a chat to a message", zap.Error(err)) - continue - } - message.LocalChatID = chat.ID - result = append(result, message) - } - return result, nil -} - -func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, error) { +func (p *postProcessor) matchMessage(message *Message, chats map[string]*Chat) (*Chat, error) { if message.SigPubKey == nil { p.logger.Error("public key can't be empty") return nil, errors.New("received a message with empty public key") @@ -1219,7 +1621,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // For public messages, all outgoing and incoming messages have the same chatID // equal to a public chat name. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { return nil, errors.New("received a public message from non-existing chat") } @@ -1228,7 +1630,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // It's a private message coming from us so we rely on Message.ChatId // If chat does not exist, it should be created to support multidevice synchronization. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { if len(chatID) != PubKeyStringLength { return nil, errors.New("invalid pubkey length") @@ -1244,9 +1646,6 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er } newChat := CreateOneToOneChat(chatID[:8], pubKey) - if err := p.persistence.SaveChat(newChat); err != nil { - return nil, errors.Wrap(err, "failed to save newly created chat") - } chat = &newChat } return chat, nil @@ -1254,13 +1653,10 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // It's an incoming private message. ChatID is calculated from the signature. // If a chat does not exist, a new one is created and saved. chatID := types.EncodeHex(crypto.FromECDSAPub(message.SigPubKey)) - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { // TODO: this should be a three-word name used in the mobile client newChat := CreateOneToOneChat(chatID[:8], message.SigPubKey) - if err := p.persistence.SaveChat(newChat); err != nil { - return nil, errors.Wrap(err, "failed to save newly created chat") - } chat = &newChat } return chat, nil @@ -1268,7 +1664,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // In the case of a group message, ChatID is the same for all messages belonging to a group. // It needs to be verified if the signature public key belongs to the chat. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { return nil, errors.New("received group chat message for non-existing chat") } @@ -1305,6 +1701,8 @@ func Identicon(id string) (string, error) { // VerifyENSNames verifies that a registered ENS name matches the expected public key func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetails []enstypes.ENSDetails) (map[string]enstypes.ENSResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() verifier := m.node.NewENSVerifier(m.logger) ensResponse, err := verifier.CheckBatch(ensDetails, rpcEndpoint, contractAddress) @@ -1316,9 +1714,12 @@ func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetai var contacts []*Contact for _, details := range ensResponse { if details.Error == nil { - contact, err := buildContact(details.PublicKey) - if err != nil { - return nil, err + contact, ok := m.allContacts[details.PublicKeyString] + if !ok { + contact, err = buildContact(details.PublicKey) + if err != nil { + return nil, err + } } contact.ENSVerified = details.Verified contact.ENSVerifiedAt = details.VerifiedAt @@ -1335,7 +1736,7 @@ func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetai } if len(contacts) != 0 { - err = m.persistence.SetContactsENSData(contacts) + err = m.persistence.SaveContacts(contacts) if err != nil { return nil, err } diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 284dce72d..d5136e506 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -21,6 +21,7 @@ import ( "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/tt" + v1protocol "github.com/status-im/status-go/protocol/v1" "github.com/status-im/status-go/whisper/v6" "github.com/stretchr/testify/suite" "go.uber.org/zap" @@ -139,7 +140,7 @@ func (s *MessengerSuite) TestInit() { ID: "some-public-chat", Active: true, } - err := s.m.SaveChat(publicChat) + err := s.m.SaveChat(&publicChat) s.Require().NoError(err) }, AddedFilters: 1, @@ -150,12 +151,11 @@ func (s *MessengerSuite) TestInit() { key, err := crypto.GenerateKey() s.Require().NoError(err) privateChat := Chat{ - ID: types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)), - ChatType: ChatTypeOneToOne, - PublicKey: &key.PublicKey, - Active: true, + ID: types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)), + ChatType: ChatTypeOneToOne, + Active: true, } - err = s.m.SaveChat(privateChat) + err = s.m.SaveChat(&privateChat) s.Require().NoError(err) }, AddedFilters: 1, @@ -179,7 +179,7 @@ func (s *MessengerSuite) TestInit() { }, }, } - err = s.m.SaveChat(groupChat) + err = s.m.SaveChat(&groupChat) s.Require().NoError(err) }, AddedFilters: 2, @@ -192,7 +192,7 @@ func (s *MessengerSuite) TestInit() { ID: "some-public-chat-2", Active: false, } - err := s.m.SaveChat(publicChat) + err := s.m.SaveChat(&publicChat) s.Require().NoError(err) }, AddedFilters: 0, @@ -207,7 +207,7 @@ func (s *MessengerSuite) TestInit() { Name: "Some Contact", SystemTags: []string{contactAdded}, } - err = s.m.SaveContact(contact) + err = s.m.SaveContact(&contact) s.Require().NoError(err) }, AddedFilters: 1, @@ -222,7 +222,7 @@ func (s *MessengerSuite) TestInit() { Name: "Some Contact", SystemTags: []string{contactAdded, contactBlocked}, } - err = s.m.SaveContact(contact) + err = s.m.SaveContact(&contact) s.Require().NoError(err) }, AddedFilters: 0, @@ -237,7 +237,7 @@ func (s *MessengerSuite) TestInit() { Name: "Some Contact", SystemTags: []string{contactRequestReceived}, } - err = s.m.SaveContact(contact) + err = s.m.SaveContact(&contact) s.Require().NoError(err) }, AddedFilters: 0, @@ -262,7 +262,10 @@ func buildTestMessage(chat Chat) *Message { message := &Message{} message.Text = "text-input-message" message.ChatId = chat.ID + message.Clock = 2 + message.WhisperTimestamp = 10 message.LocalChatID = chat.ID + message.ContentType = protobuf.ChatMessage_TEXT_PLAIN switch chat.ChatType { case ChatTypePublic: message.MessageType = protobuf.ChatMessage_PUBLIC_GROUP @@ -278,7 +281,7 @@ func buildTestMessage(chat Chat) *Message { func (s *MessengerSuite) TestMarkMessagesSeen() { chat := CreatePublicChat("test-chat") chat.UnviewedMessagesCount = 2 - err := s.m.SaveChat(chat) + err := s.m.SaveChat(&chat) s.Require().NoError(err) inputMessage1 := buildTestMessage(chat) inputMessage1.ID = "1" @@ -293,8 +296,7 @@ func (s *MessengerSuite) TestMarkMessagesSeen() { err = s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID}) s.Require().NoError(err) - chats, err := s.m.Chats() - s.Require().NoError(err) + chats := s.m.Chats() s.Require().Len(chats, 1) s.Require().Equal(uint(1), chats[0].UnviewedMessagesCount) } @@ -302,7 +304,7 @@ func (s *MessengerSuite) TestMarkMessagesSeen() { func (s *MessengerSuite) TestSendPublic() { chat := CreatePublicChat("test-chat") chat.LastClockValue = uint64(100000000000000) - err := s.m.SaveChat(chat) + err := s.m.SaveChat(&chat) s.NoError(err) inputMessage := buildTestMessage(chat) response, err := s.m.SendChatMessage(context.Background(), inputMessage) @@ -311,7 +313,8 @@ func (s *MessengerSuite) TestSendPublic() { s.Require().Equal(1, len(response.Messages), "it returns the message") outputMessage := response.Messages[0] - s.Require().Equal(chat.LastClockValue+1, outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), chat.LastClockValue, "it correctly sets the last-clock-value") s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") @@ -333,14 +336,16 @@ func (s *MessengerSuite) TestSendPrivateOneToOne() { inputMessage := &Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.NoError(err) response, err := s.m.SendChatMessage(context.Background(), inputMessage) s.NoError(err) s.Require().Equal(1, len(response.Messages), "it returns the message") outputMessage := response.Messages[0] - s.Require().Equal(chat.LastClockValue+1, outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), chat.LastClockValue, "it correctly sets the last-clock-value") + s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") @@ -349,50 +354,31 @@ func (s *MessengerSuite) TestSendPrivateOneToOne() { s.Require().Equal(protobuf.ChatMessage_ONE_TO_ONE, outputMessage.MessageType) } -func (s *MessengerSuite) TestAddSystemMessages() { - chat, err := s.m.CreateGroupChat("test") - s.NoError(err) - inputMessage := buildTestMessage(*chat) - inputMessage.Clock = 20 - inputMessage.From = "0x" + hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)) - messages, err := s.m.AddSystemMessages([]*Message{inputMessage}) - s.Require().NoError(err) - s.Require().Len(messages, 1) - - actualMessage := messages[0] - - s.Require().NotEmpty(actualMessage.ID) - s.Require().True(actualMessage.Seen) - s.Require().Empty(actualMessage.OutgoingStatus) - s.Require().NotEmpty(actualMessage.Timestamp) - s.Require().NotEmpty(actualMessage.WhisperTimestamp) - s.Require().Equal(chat.ID, actualMessage.LocalChatID) - s.Require().NotEmpty(actualMessage.Identicon) - s.Require().NotEmpty(actualMessage.Alias) - s.Require().Equal(protobuf.ChatMessage_STATUS, actualMessage.ContentType) - s.Require().Equal(protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP, actualMessage.MessageType) - s.Require().NotEmpty(actualMessage.ParsedText) -} - func (s *MessengerSuite) TestSendPrivateGroup() { - chat, err := s.m.CreateGroupChat("test") + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", []string{}) s.NoError(err) + s.Require().Len(response.Chats, 1) + + chat := response.Chats[0] key, err := crypto.GenerateKey() s.NoError(err) - err = s.m.AddMembersToChat(context.Background(), chat, []*ecdsa.PublicKey{&key.PublicKey}) + members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey))} + _, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members) s.NoError(err) inputMessage := &Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) - err = s.m.SaveChat(*chat) + err = s.m.SaveChat(chat) s.NoError(err) - response, err := s.m.SendChatMessage(context.Background(), inputMessage) + response, err = s.m.SendChatMessage(context.Background(), inputMessage) s.NoError(err) s.Require().Equal(1, len(response.Messages), "it returns the message") outputMessage := response.Messages[0] - s.Require().Equal(chat.LastClockValue+1, outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), chat.LastClockValue, "it correctly sets the last-clock-value") + s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") @@ -402,20 +388,25 @@ func (s *MessengerSuite) TestSendPrivateGroup() { } func (s *MessengerSuite) TestSendPrivateEmptyGroup() { - chat, err := s.m.CreateGroupChat("test") + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", []string{}) s.NoError(err) + s.Require().Len(response.Chats, 1) + + chat := response.Chats[0] inputMessage := &Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) - err = s.m.SaveChat(*chat) + err = s.m.SaveChat(chat) s.NoError(err) - response, err := s.m.SendChatMessage(context.Background(), inputMessage) + response, err = s.m.SendChatMessage(context.Background(), inputMessage) s.NoError(err) s.Require().Equal(1, len(response.Messages), "it returns the message") outputMessage := response.Messages[0] - s.Require().Equal(chat.LastClockValue+1, outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), outputMessage.Clock, "it correctly sets the clock") + s.Require().Equal(uint64(100000000000001), chat.LastClockValue, "it correctly sets the last-clock-value") + s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") @@ -427,7 +418,7 @@ func (s *MessengerSuite) TestSendPrivateEmptyGroup() { // Make sure public messages sent by us are not func (s *MessengerSuite) TestRetrieveOwnPublic() { chat := CreatePublicChat("status") - err := s.m.SaveChat(chat) + err := s.m.SaveChat(&chat) s.NoError(err) // Right-to-left text text := "پيل اندر خانه يي تاريک بود عرضه را آورده بودندش هنود i\nاز براي ديدنش مردم بسي اندر آن ظلمت همي شد هر کسي" @@ -462,11 +453,11 @@ func (s *MessengerSuite) TestRetrieveOwnPublic() { func (s *MessengerSuite) TestRetrieveTheirPublic() { theirMessenger := s.newMessenger(s.shh) theirChat := CreatePublicChat("status") - err := theirMessenger.SaveChat(theirChat) + err := theirMessenger.SaveChat(&theirChat) s.Require().NoError(err) chat := CreatePublicChat("status") - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.Require().NoError(err) err = s.m.Join(chat) @@ -505,11 +496,11 @@ func (s *MessengerSuite) TestRetrieveTheirPublic() { func (s *MessengerSuite) TestDeletedAtClockValue() { theirMessenger := s.newMessenger(s.shh) theirChat := CreatePublicChat("status") - err := theirMessenger.SaveChat(theirChat) + err := theirMessenger.SaveChat(&theirChat) s.Require().NoError(err) chat := CreatePublicChat("status") - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.Require().NoError(err) err = s.m.Join(chat) @@ -521,7 +512,7 @@ func (s *MessengerSuite) TestDeletedAtClockValue() { s.NoError(err) chat.DeletedAtClockValue = sentResponse.Messages[0].Clock - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.Require().NoError(err) // Wait for the message to reach its destination @@ -534,11 +525,11 @@ func (s *MessengerSuite) TestDeletedAtClockValue() { func (s *MessengerSuite) TestRetrieveBlockedContact() { theirMessenger := s.newMessenger(s.shh) theirChat := CreatePublicChat("status") - err := theirMessenger.SaveChat(theirChat) + err := theirMessenger.SaveChat(&theirChat) s.Require().NoError(err) chat := CreatePublicChat("status") - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.Require().NoError(err) err = s.m.Join(chat) @@ -555,7 +546,7 @@ func (s *MessengerSuite) TestRetrieveBlockedContact() { TributeToTalk: "talk", } - s.Require().NoError(s.m.SaveContact(blockedContact)) + s.Require().NoError(s.m.SaveContact(&blockedContact)) inputMessage := buildTestMessage(chat) @@ -573,11 +564,11 @@ func (s *MessengerSuite) TestRetrieveBlockedContact() { func (s *MessengerSuite) TestResendPublicMessage() { theirMessenger := s.newMessenger(s.shh) theirChat := CreatePublicChat("status") - err := theirMessenger.SaveChat(theirChat) + err := theirMessenger.SaveChat(&theirChat) s.Require().NoError(err) chat := CreatePublicChat("status") - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.Require().NoError(err) err = s.m.Join(chat) @@ -632,14 +623,14 @@ func (s *MessengerSuite) TestResendPublicMessage() { func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() { theirMessenger := s.newMessenger(s.shh) theirChat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey) - err := theirMessenger.SaveChat(theirChat) + err := theirMessenger.SaveChat(&theirChat) s.Require().NoError(err) ourChat := CreateOneToOneChat("our-chat", &theirMessenger.identity.PublicKey) ourChat.UnviewedMessagesCount = 1 // Make chat inactive ourChat.Active = false - err = s.m.SaveChat(ourChat) + err = s.m.SaveChat(&ourChat) s.Require().NoError(err) inputMessage := buildTestMessage(theirChat) @@ -676,7 +667,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() { func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() { theirMessenger := s.newMessenger(s.shh) chat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey) - err := theirMessenger.SaveChat(chat) + err := theirMessenger.SaveChat(&chat) s.NoError(err) inputMessage := buildTestMessage(chat) @@ -715,7 +706,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() { func (s *MessengerSuite) TestRetrieveOurPairedMessage() { pairedMessenger := s.newMessengerWithKey(s.shh, s.privateKey) chat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey) - err := pairedMessenger.SaveChat(chat) + err := pairedMessenger.SaveChat(&chat) s.NoError(err) inputMessage := buildTestMessage(chat) @@ -765,7 +756,7 @@ func (s *MessengerSuite) TestRetrieveOurPairedMessage() { key, err := crypto.GenerateKey() s.Require().NoError(err) chat = CreateOneToOneChat("new-chat", &key.PublicKey) - err = s.m.SaveChat(chat) + err = s.m.SaveChat(&chat) s.NoError(err) inputMessage = buildTestMessage(chat) @@ -799,7 +790,7 @@ func (s *MessengerSuite) TestRetrieveOurPairedMessage() { func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() { theirMessenger := s.newMessenger(s.shh) chat := CreatePublicChat("test-chat") - err := theirMessenger.SaveChat(chat) + err := theirMessenger.SaveChat(&chat) s.NoError(err) inputMessage := buildTestMessage(chat) @@ -817,13 +808,16 @@ func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() { s.Require().Equal(len(response.Chats), 0) } -// Test receiving a message on an non-existing public chat +// Test receiving a message on an non-existing private public chat func (s *MessengerSuite) TestRetrieveTheirGroupChatNonExisting() { theirMessenger := s.newMessenger(s.shh) - chat, err := theirMessenger.CreateGroupChat("test") + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", []string{}) s.NoError(err) + s.Require().Len(response.Chats, 1) - err = theirMessenger.SaveChat(*chat) + chat := response.Chats[0] + + err = theirMessenger.SaveChat(chat) s.NoError(err) inputMessage := buildTestMessage(*chat) @@ -832,31 +826,62 @@ func (s *MessengerSuite) TestRetrieveTheirGroupChatNonExisting() { s.NoError(err) s.Require().Len(sendResponse.Messages, 1) - // Wait for the message to reach its destination - time.Sleep(100 * time.Millisecond) - response, err := s.m.RetrieveAll() - s.NoError(err) + // Retrieve their messages so that the chat is created + err = tt.RetryWithBackOff(func() error { + var err error + response, err = s.m.RetrieveAll() + if err == nil && len(response.Chats) == 1 { + err = errors.New("chat membership update not received") + } + return err + }) + s.Require().NoError(err) - s.Require().Equal(len(response.Messages), 0) - s.Require().Equal(len(response.Chats), 0) + // The message is discarded + s.Require().Equal(0, len(response.Messages)) + s.Require().Equal(0, len(response.Chats)) } // Test receiving a message on an existing private group chat -// Disable for now -func (s *MessengerSuite) testRetrieveTheirPrivateGroupChat() { +func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() { + var response *MessengerResponse theirMessenger := s.newMessenger(s.shh) - ourChat, err := s.m.CreateGroupChat("id") - err = s.m.SaveChat(*ourChat) + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "id", []string{}) + s.NoError(err) + s.Require().Len(response.Chats, 1) + + ourChat := response.Chats[0] + + err = s.m.SaveChat(ourChat) s.NoError(err) - err = s.m.AddMembersToChat(context.Background(), ourChat, []*ecdsa.PublicKey{&theirMessenger.identity.PublicKey}) + members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))} + _, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members) s.NoError(err) - err = theirMessenger.SaveChat(*ourChat) + // Retrieve their messages so that the chat is created + err = tt.RetryWithBackOff(func() error { + var err error + response, err = theirMessenger.RetrieveAll() + if err == nil && len(response.Chats) == 0 { + err = errors.New("chat invitation not received") + } + return err + }) + s.Require().NoError(err) + + _, err = theirMessenger.ConfirmJoiningGroup(context.Background(), ourChat.ID) s.NoError(err) - err = theirMessenger.Join(*ourChat) - s.NoError(err) + err = tt.RetryWithBackOff(func() error { + var err error + response, err = s.m.RetrieveAll() + if err == nil && len(response.Chats) == 0 { + err = errors.New("no joining group event received") + } + return err + }) + s.Require().NoError(err) inputMessage := buildTestMessage(*ourChat) @@ -866,7 +891,6 @@ func (s *MessengerSuite) testRetrieveTheirPrivateGroupChat() { sentMessage := sendResponse.Messages[0] - var response *MessengerResponse err = tt.RetryWithBackOff(func() error { var err error response, err = s.m.RetrieveAll() @@ -887,14 +911,65 @@ func (s *MessengerSuite) testRetrieveTheirPrivateGroupChat() { s.Require().NotNil(actualChat.LastMessage) } -// Test it does not update the last message if clock value less then -// Test it does not return messages from blocked contacts -// Test it saves the messages -// Test it does not return the message for public if no chat is there -// Test returns contacts -// Test it does not return raw messages if all processed -// Test duplicate messages, don't update unviewed messages count, they are -// not passed back +// Test receiving a message on an existing private group chat, if messages +// are not wrapped this will fail as they'll likely come out of order +func (s *MessengerSuite) TestRetrieveTheirPrivateGroupWrappedMessageChat() { + var response *MessengerResponse + theirMessenger := s.newMessenger(s.shh) + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "id", []string{}) + s.NoError(err) + s.Require().Len(response.Chats, 1) + + ourChat := response.Chats[0] + + err = s.m.SaveChat(ourChat) + s.NoError(err) + + members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))} + _, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members) + s.NoError(err) + + // Retrieve their messages so that the chat is created + err = tt.RetryWithBackOff(func() error { + var err error + response, err = theirMessenger.RetrieveAll() + if err == nil && len(response.Chats) == 0 { + err = errors.New("chat invitation not received") + } + return err + }) + s.Require().NoError(err) + + _, err = theirMessenger.ConfirmJoiningGroup(context.Background(), ourChat.ID) + s.NoError(err) + + inputMessage := buildTestMessage(*ourChat) + + sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage) + s.NoError(err) + s.Require().Len(sendResponse.Messages, 1) + + sentMessage := sendResponse.Messages[0] + + err = tt.RetryWithBackOff(func() error { + var err error + response, err = s.m.RetrieveAll() + if err == nil && len(response.Messages) == 0 { + err = errors.New("no messages") + } + return err + }) + s.Require().NoError(err) + + s.Require().Len(response.Chats, 1) + actualChat := response.Chats[0] + // It updates the unviewed messages count + s.Require().Equal(uint(1), actualChat.UnviewedMessagesCount) + // It updates the last message clock value + s.Require().Equal(sentMessage.Clock, actualChat.LastClockValue) + // It sets the last message + s.Require().NotNil(actualChat.LastMessage) +} func (s *MessengerSuite) TestChatPersistencePublic() { chat := Chat{ @@ -910,9 +985,8 @@ func (s *MessengerSuite) TestChatPersistencePublic() { LastMessage: []byte("test"), } - s.Require().NoError(s.m.SaveChat(chat)) - savedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + savedChats := s.m.Chats() s.Require().Equal(1, len(savedChats)) actualChat := savedChats[0] @@ -936,14 +1010,12 @@ func (s *MessengerSuite) TestDeleteChat() { LastMessage: []byte("test"), } - s.Require().NoError(s.m.SaveChat(chat)) - savedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + savedChats := s.m.Chats() s.Require().Equal(1, len(savedChats)) s.Require().NoError(s.m.DeleteChat(chatID)) - savedChats, err = s.m.Chats() - s.Require().NoError(err) + savedChats = s.m.Chats() s.Require().Equal(0, len(savedChats)) } @@ -961,9 +1033,8 @@ func (s *MessengerSuite) TestChatPersistenceUpdate() { LastMessage: []byte("test"), } - s.Require().NoError(s.m.SaveChat(chat)) - savedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + savedChats := s.m.Chats() s.Require().Equal(1, len(savedChats)) actualChat := savedChats[0] @@ -972,9 +1043,8 @@ func (s *MessengerSuite) TestChatPersistenceUpdate() { s.Require().Equal(expectedChat, actualChat) chat.Name = "updated-name" - s.Require().NoError(s.m.SaveChat(chat)) - updatedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + updatedChats := s.m.Chats() s.Require().Equal(1, len(updatedChats)) actualUpdatedChat := updatedChats[0] @@ -1003,14 +1073,17 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() { pk, err := crypto.UnmarshalPubkey(publicKeyBytes) s.Require().NoError(err) - s.Require().NoError(s.m.SaveChat(chat)) - savedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + savedChats := s.m.Chats() s.Require().Equal(1, len(savedChats)) actualChat := savedChats[0] expectedChat := &chat - expectedChat.PublicKey = pk + + actualPk, err := actualChat.PublicKey() + s.Require().NoError(err) + + s.Require().Equal(pk, actualPk) s.Require().Equal(expectedChat, actualChat) } @@ -1040,25 +1113,21 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { Joined: true, }, }, - MembershipUpdates: []ChatMembershipUpdate{ - ChatMembershipUpdate{ - ID: "1", - Type: "type-1", + MembershipUpdates: []v1protocol.MembershipUpdateEvent{ + { + Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, Name: "name-1", ClockValue: 1, - Signature: "signature-1", + Signature: []byte("signature-1"), From: "from-1", - Member: "member-1", Members: []string{"member-1", "member-2"}, }, - ChatMembershipUpdate{ - ID: "2", - Type: "type-2", + { + Type: protobuf.MembershipUpdateEvent_MEMBERS_ADDED, Name: "name-2", ClockValue: 2, - Signature: "signature-2", + Signature: []byte("signature-2"), From: "from-2", - Member: "member-2", Members: []string{"member-2", "member-3"}, }, }, @@ -1067,9 +1136,8 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { UnviewedMessagesCount: 40, LastMessage: []byte("test"), } - s.Require().NoError(s.m.SaveChat(chat)) - savedChats, err := s.m.Chats() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveChat(&chat)) + savedChats := s.m.Chats() s.Require().Equal(1, len(savedChats)) actualChat := savedChats[0] @@ -1139,11 +1207,11 @@ func (s *MessengerSuite) TestBlockContact() { UnviewedMessagesCount: 40, } - s.Require().NoError(s.m.SaveChat(chat1)) - s.Require().NoError(s.m.SaveChat(chat2)) - s.Require().NoError(s.m.SaveChat(chat3)) + s.Require().NoError(s.m.SaveChat(&chat1)) + s.Require().NoError(s.m.SaveChat(&chat2)) + s.Require().NoError(s.m.SaveChat(&chat3)) - s.Require().NoError(s.m.SaveContact(contact)) + s.Require().NoError(s.m.SaveContact(&contact)) contact.Name = "blocked" @@ -1228,7 +1296,7 @@ func (s *MessengerSuite) TestBlockContact() { err := s.m.SaveMessages(messages) s.Require().NoError(err) - response, err := s.m.BlockContact(contact) + response, err := s.m.BlockContact(&contact) s.Require().NoError(err) // The new unviewed count is updated @@ -1249,14 +1317,12 @@ func (s *MessengerSuite) TestBlockContact() { s.Require().Equal("test-5", decodedMessage.ID) // The contact is updated - savedContacts, err := s.m.Contacts() - s.Require().NoError(err) + savedContacts := s.m.Contacts() s.Require().Equal(1, len(savedContacts)) s.Require().Equal("blocked", savedContacts[0].Name) // The chat is deleted - actualChats, err := s.m.Chats() - s.Require().NoError(err) + actualChats := s.m.Chats() s.Require().Equal(2, len(actualChats)) // The messages have been deleted @@ -1294,9 +1360,8 @@ func (s *MessengerSuite) TestContactPersistence() { TributeToTalk: "talk", } - s.Require().NoError(s.m.SaveContact(contact)) - savedContacts, err := s.m.Contacts() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveContact(&contact)) + savedContacts := s.m.Contacts() s.Require().Equal(1, len(savedContacts)) actualContact := savedContacts[0] @@ -1354,8 +1419,7 @@ func (s *MessengerSuite) TestVerifyENSNames() { s.Require().False(response[pk4].Verified) // The contacts are updated - savedContacts, err := s.m.Contacts() - s.Require().NoError(err) + savedContacts := s.m.Contacts() s.Require().Equal(2, len(savedContacts)) @@ -1404,9 +1468,8 @@ func (s *MessengerSuite) TestContactPersistenceUpdate() { TributeToTalk: "talk", } - s.Require().NoError(s.m.SaveContact(contact)) - savedContacts, err := s.m.Contacts() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveContact(&contact)) + savedContacts := s.m.Contacts() s.Require().Equal(1, len(savedContacts)) actualContact := savedContacts[0] @@ -1418,9 +1481,8 @@ func (s *MessengerSuite) TestContactPersistenceUpdate() { s.Require().Equal(expectedContact, actualContact) contact.Name = "updated-name" - s.Require().NoError(s.m.SaveContact(contact)) - updatedContact, err := s.m.Contacts() - s.Require().NoError(err) + s.Require().NoError(s.m.SaveContact(&contact)) + updatedContact := s.m.Contacts() s.Require().Equal(1, len(updatedContact)) actualUpdatedContact := updatedContact[0] @@ -1434,22 +1496,39 @@ func (s *MessengerSuite) TestSharedSecretHandler() { s.NoError(err) } -func (s *MessengerSuite) TestCreateGroupChat() { - chat, err := s.m.CreateGroupChat("test") - s.Require().NoError(err) +func (s *MessengerSuite) TestCreateGroupChatWithMembers() { + members := []string{"0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1"} + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", members) + s.NoError(err) + s.Require().Len(response.Chats, 1) + + chat := response.Chats[0] + s.Require().Equal("test", chat.Name) publicKeyHex := "0x" + hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) s.Require().Contains(chat.ID, publicKeyHex) s.EqualValues([]string{publicKeyHex}, []string{chat.Members[0].ID}) + s.Equal(members[0], chat.Members[1].ID) } func (s *MessengerSuite) TestAddMembersToChat() { - chat, err := s.m.CreateGroupChat("test") + response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", []string{}) s.Require().NoError(err) + s.Require().Len(response.Chats, 1) + + chat := response.Chats[0] + key, err := crypto.GenerateKey() s.Require().NoError(err) - err = s.m.AddMembersToChat(context.Background(), chat, []*ecdsa.PublicKey{&key.PublicKey}) + members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey))} + + response, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members) s.Require().NoError(err) + s.Require().Len(response.Chats, 1) + s.Require().Len(response.Messages, 1) + + chat = response.Chats[0] + publicKeyHex := "0x" + hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) keyHex := "0x" + hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey)) s.EqualValues([]string{publicKeyHex, keyHex}, []string{chat.Members[0].ID, chat.Members[1].ID}) @@ -1550,11 +1629,12 @@ func (s *PostProcessorSuite) TestRun() { s.Require().NoError(err) testCases := []struct { - Name string - Chat Chat // Chat to create - Message Message - SigPubKey *ecdsa.PublicKey - ExpectedChatIDs []string + Name string + Error bool + Chat Chat // Chat to create + Message Message + SigPubKey *ecdsa.PublicKey + ExpectedChatID string }{ { Name: "Public chat", @@ -1565,8 +1645,8 @@ func (s *PostProcessorSuite) TestRun() { MessageType: protobuf.ChatMessage_PUBLIC_GROUP, Text: "test-text"}, }, - SigPubKey: &key1.PublicKey, - ExpectedChatIDs: []string{"test-chat"}, + SigPubKey: &key1.PublicKey, + ExpectedChatID: "test-chat", }, { Name: "Private message from myself with existing chat", @@ -1577,8 +1657,8 @@ func (s *PostProcessorSuite) TestRun() { MessageType: protobuf.ChatMessage_ONE_TO_ONE, Text: "test-text"}, }, - SigPubKey: &key1.PublicKey, - ExpectedChatIDs: []string{oneToOneChatID(&key1.PublicKey)}, + SigPubKey: &key1.PublicKey, + ExpectedChatID: oneToOneChatID(&key1.PublicKey), }, { Name: "Private message from other with existing chat", @@ -1590,8 +1670,8 @@ func (s *PostProcessorSuite) TestRun() { Text: "test-text"}, }, - SigPubKey: &key2.PublicKey, - ExpectedChatIDs: []string{oneToOneChatID(&key2.PublicKey)}, + SigPubKey: &key2.PublicKey, + ExpectedChatID: oneToOneChatID(&key2.PublicKey), }, { Name: "Private message from myself without chat", @@ -1602,8 +1682,8 @@ func (s *PostProcessorSuite) TestRun() { Text: "test-text"}, }, - SigPubKey: &key1.PublicKey, - ExpectedChatIDs: []string{oneToOneChatID(&key1.PublicKey)}, + SigPubKey: &key1.PublicKey, + ExpectedChatID: oneToOneChatID(&key1.PublicKey), }, { Name: "Private message from other without chat", @@ -1614,12 +1694,13 @@ func (s *PostProcessorSuite) TestRun() { Text: "test-text"}, }, - SigPubKey: &key2.PublicKey, - ExpectedChatIDs: []string{oneToOneChatID(&key2.PublicKey)}, + SigPubKey: &key2.PublicKey, + ExpectedChatID: oneToOneChatID(&key2.PublicKey), }, { Name: "Private message without public key", SigPubKey: nil, + Error: true, }, { Name: "Private group message", @@ -1629,15 +1710,16 @@ func (s *PostProcessorSuite) TestRun() { MessageType: protobuf.ChatMessage_PRIVATE_GROUP, Text: "test-text"}, }, + Error: true, SigPubKey: &key2.PublicKey, }, - - // TODO: add test for group messages } for idx, tc := range testCases { s.Run(tc.Name, func() { + chatsMap := make(map[string]*Chat) if tc.Chat.ID != "" { + chatsMap[tc.Chat.ID] = &tc.Chat err := s.postProcessor.persistence.SaveChat(tc.Chat) s.Require().NoError(err) defer func() { @@ -1652,12 +1734,16 @@ func (s *PostProcessorSuite) TestRun() { s.Empty(message.LocalChatID) message.ID = strconv.Itoa(idx) // manually set the ID because messages does not go through messageProcessor - messages, err := s.postProcessor.matchMessages([]*Message{&message}) - s.NoError(err) - s.Require().Len(messages, len(tc.ExpectedChatIDs)) - if len(tc.ExpectedChatIDs) != 0 { - s.Equal(tc.ExpectedChatIDs[0], message.LocalChatID) - s.EqualValues(&message, messages[0]) + chat, err := s.postProcessor.matchMessage(&message, chatsMap) + if tc.Error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + if tc.ExpectedChatID != "" { + + s.Require().NotNil(chat) + s.Require().Equal(tc.ExpectedChatID, chat.ID) + } } }) } diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index e000dc621..4ff613eac 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -114,7 +114,7 @@ func _000001_initUpDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000001_init.up.db.sql", size: 832, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000001_init.up.db.sql", size: 832, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0xa4, 0xac, 0x0, 0xd3, 0x19, 0x53, 0x35, 0x91, 0x1c, 0x94, 0xea, 0xde, 0xa7, 0x75, 0xb6, 0x73, 0x1d, 0x42, 0x14, 0xca, 0x84, 0x5b, 0xdb, 0x10, 0x94, 0x28, 0xc0, 0x33, 0x95, 0x7f, 0xf}} return a, nil } @@ -154,7 +154,7 @@ func _000002_add_chatsUpDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 495, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 495, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6e, 0xca, 0x2b, 0xf7, 0xca, 0x21, 0xda, 0x17, 0x1f, 0x97, 0xa8, 0x12, 0xb5, 0x6c, 0xad, 0x92, 0xe7, 0x2, 0xaf, 0x1, 0xcb, 0x5e, 0xe9, 0x71, 0xc4, 0x81, 0xa7, 0x3, 0x93, 0x5b, 0x73, 0x73}} return a, nil } @@ -234,7 +234,7 @@ func _000004_user_messages_compatibilityUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000004_user_messages_compatibility.up.sql", size: 980, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000004_user_messages_compatibility.up.sql", size: 980, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x7a, 0xba, 0xae, 0x6d, 0xef, 0x69, 0x12, 0x6b, 0x48, 0xe3, 0xa7, 0xad, 0x21, 0x4a, 0xcf, 0x4f, 0xbc, 0x14, 0xc1, 0x19, 0x69, 0x1c, 0xc, 0xa2, 0x3d, 0xbc, 0x12, 0x32, 0x71, 0x76, 0x15}} return a, nil } @@ -274,7 +274,7 @@ func _1567112142_user_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1567112142_user_messages.up.sql", size: 543, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "1567112142_user_messages.up.sql", size: 543, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xff, 0xc0, 0x47, 0x32, 0xa9, 0xa4, 0x6, 0x63, 0x6b, 0xe7, 0x79, 0x2b, 0x80, 0x52, 0x2b, 0x6f, 0xf9, 0x9d, 0x9a, 0xc2, 0xa9, 0x7a, 0xf7, 0x4d, 0x14, 0x12, 0x21, 0x10, 0xc4, 0x30, 0x42, 0xaa}} return a, nil } diff --git a/protocol/persistence.go b/protocol/persistence.go index 730be6fb1..3100b3bb1 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -5,10 +5,8 @@ import ( "context" "database/sql" "encoding/gob" - "encoding/hex" "github.com/pkg/errors" - "github.com/status-im/status-go/eth-node/crypto" ) var ( @@ -45,6 +43,26 @@ func (db sqlitePersistence) SaveChats(chats []*Chat) error { return nil } +func (db sqlitePersistence) SaveContacts(contacts []*Contact) error { + tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) + defer func() { + if err == nil { + err = tx.Commit() + return + } + // don't shadow original error + _ = tx.Rollback() + }() + + for _, contact := range contacts { + err := db.SaveContact(contact, tx) + if err != nil { + return err + } + } + return nil +} + func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { var err error if tx == nil { @@ -62,21 +80,6 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { }() } - pkey := []byte{} - // For one to one chatID is an encoded public key - if chat.ChatType == ChatTypeOneToOne { - pkey, err = hex.DecodeString(chat.ID[2:]) - if err != nil { - return err - } - // Safety check, make sure is well formed - _, err := crypto.UnmarshalPubkey(pkey) - if err != nil { - return err - } - - } - // Encode members var encodedMembers bytes.Buffer memberEncoder := gob.NewEncoder(&encodedMembers) @@ -94,8 +97,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { } // Insert record - stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, public_key, unviewed_message_count, last_clock_value, last_message, members, membership_updates) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) + stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { return err } @@ -109,7 +112,6 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { chat.ChatType, chat.Timestamp, chat.DeletedAtClockValue, - pkey, chat.UnviewedMessagesCount, chat.LastClockValue, chat.LastMessage, @@ -157,7 +159,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { type, timestamp, deleted_at_clock_value, - public_key, unviewed_message_count, last_clock_value, last_message, @@ -176,7 +177,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { chat Chat encodedMembers []byte encodedMembershipUpdates []byte - pkey []byte ) err = rows.Scan( &chat.ID, @@ -186,7 +186,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { &chat.ChatType, &chat.Timestamp, &chat.DeletedAtClockValue, - &pkey, &chat.UnviewedMessagesCount, &chat.LastClockValue, &chat.LastMessage, @@ -211,18 +210,73 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { return } - if len(pkey) != 0 { - chat.PublicKey, err = crypto.UnmarshalPubkey(pkey) - if err != nil { - return - } - } chats = append(chats, &chat) } return } +func (db sqlitePersistence) Chat(chatID string) (*Chat, error) { + var ( + chat Chat + encodedMembers []byte + encodedMembershipUpdates []byte + ) + + err := db.db.QueryRow(` + SELECT + id, + name, + color, + active, + type, + timestamp, + deleted_at_clock_value, + unviewed_message_count, + last_clock_value, + last_message, + members, + membership_updates + FROM chats + WHERE id = ? + `, chatID).Scan(&chat.ID, + &chat.Name, + &chat.Color, + &chat.Active, + &chat.ChatType, + &chat.Timestamp, + &chat.DeletedAtClockValue, + &chat.UnviewedMessagesCount, + &chat.LastClockValue, + &chat.LastMessage, + &encodedMembers, + &encodedMembershipUpdates, + ) + switch err { + case sql.ErrNoRows: + return nil, nil + case nil: + // Restore members + membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers)) + err = membersDecoder.Decode(&chat.Members) + if err != nil { + return nil, err + } + + // Restore membership updates + membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates)) + err = membershipUpdatesDecoder.Decode(&chat.MembershipUpdates) + if err != nil { + return nil, err + } + + return &chat, nil + } + + return nil, err + +} + func (db sqlitePersistence) Contacts() ([]*Contact, error) { rows, err := db.db.Query(` SELECT @@ -293,83 +347,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { return response, nil } -func (db sqlitePersistence) SetContactsENSData(contacts []*Contact) error { - tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) - if err != nil { - return err - } - defer func() { - if err == nil { - err = tx.Commit() - return - } - // don't shadow original error - _ = tx.Rollback() - }() - - // Ensure contacts exists - - err = db.SetContactsGeneratedData(contacts, tx) - if err != nil { - return err - } - - // Update ens data - for _, contact := range contacts { - _, err := tx.Exec(`UPDATE contacts SET name = ?, ens_verified = ? , ens_verified_at = ? WHERE id = ?`, contact.Name, contact.ENSVerified, contact.ENSVerifiedAt, contact.ID) - if err != nil { - return err - } - } - - return nil -} - -// SetContactsGeneratedData sets a contact generated data if not existing already -// in the database -func (db sqlitePersistence) SetContactsGeneratedData(contacts []*Contact, tx *sql.Tx) (err error) { - if tx == nil { - tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) - if err != nil { - return err - } - defer func() { - if err == nil { - err = tx.Commit() - return - - } - // don't shadow original error - _ = tx.Rollback() - }() - } - - for _, contact := range contacts { - _, err = tx.Exec(` - INSERT OR IGNORE INTO contacts( - id, - address, - name, - alias, - identicon, - photo, - last_updated, - tribute_to_talk - ) VALUES (?, ?, "", ?, ?, "", 0, "")`, - contact.ID, - contact.Address, - contact.Alias, - contact.Identicon, - ) - if err != nil { - return - } - } - - return -} - -func (db sqlitePersistence) SaveContact(contact Contact, tx *sql.Tx) (err error) { +func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error) { if tx == nil { tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { diff --git a/protocol/persistence_legacy.go b/protocol/persistence_legacy.go index 362b27bb3..de530c95c 100644 --- a/protocol/persistence_legacy.go +++ b/protocol/persistence_legacy.go @@ -406,7 +406,7 @@ func (db sqlitePersistence) UpdateMessageOutgoingStatus(id string, newOutgoingSt } // BlockContact updates a contact, deletes all the messages and 1-to-1 chat, updates the unread messages count and returns a map with the new count -func (db sqlitePersistence) BlockContact(contact Contact) ([]*Chat, error) { +func (db sqlitePersistence) BlockContact(contact *Contact) ([]*Chat, error) { var chats []*Chat tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { diff --git a/protocol/persistence_legacy_test.go b/protocol/persistence_legacy_test.go index ddb3c3fda..62765043f 100644 --- a/protocol/persistence_legacy_test.go +++ b/protocol/persistence_legacy_test.go @@ -355,62 +355,6 @@ func TestUpdateMessageOutgoingStatus(t *testing.T) { require.Equal(t, "new-status", m.OutgoingStatus) } -func TestSetContactGeneratedData(t *testing.T) { - db, err := openTestDB() - require.NoError(t, err) - p := sqlitePersistence{db: db} - existingContact := Contact{ - ID: "contact-one", - Address: "contact-address", - Name: "contact-name", - Photo: "contact-photo", - LastUpdated: 20, - SystemTags: []string{"1", "2"}, - DeviceInfo: []ContactDeviceInfo{ - ContactDeviceInfo{ - InstallationID: "1", - Timestamp: 2, - FCMToken: "token", - }, - ContactDeviceInfo{ - InstallationID: "2", - Timestamp: 3, - FCMToken: "token-2", - }, - }, - TributeToTalk: "talk", - } - - existingContactUpdate := Contact{ - ID: "contact-one", - Address: "contact-address", - Alias: "generated-name-one", - } - - nonExistingContactUpdate := Contact{ - ID: "contact-two", - Address: "contact-address", - Alias: "generated-name-two", - } - - err = p.SaveContact(existingContact, nil) - require.NoError(t, err) - - err = p.SetContactsGeneratedData([]*Contact{&existingContactUpdate, &nonExistingContactUpdate}, nil) - require.NoError(t, err) - - allContacts, err := p.Contacts() - require.NoError(t, err) - - require.Equal(t, 2, len(allContacts)) - - // Make sure it has not been modified - require.Equal(t, int64(20), allContacts[0].LastUpdated) - - // Ensure new contact has been saved - require.Equal(t, "contact-two", allContacts[1].ID) -} - func openTestDB() (*sql.DB, error) { dbPath, err := ioutil.TempFile("", "") if err != nil { diff --git a/protocol/protobuf/application_metadata_message.pb.go b/protocol/protobuf/application_metadata_message.pb.go new file mode 100644 index 000000000..ac37121f0 --- /dev/null +++ b/protocol/protobuf/application_metadata_message.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: application_metadata_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type ApplicationMetadataMessage_Type int32 + +const ( + ApplicationMetadataMessage_UNKNOWN ApplicationMetadataMessage_Type = 0 + ApplicationMetadataMessage_CHAT_MESSAGE ApplicationMetadataMessage_Type = 1 + ApplicationMetadataMessage_CONTACT_REQUEST ApplicationMetadataMessage_Type = 2 + ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE ApplicationMetadataMessage_Type = 3 + ApplicationMetadataMessage_PAIR_INSTALLATION ApplicationMetadataMessage_Type = 4 + ApplicationMetadataMessage_SYNC_INSTALLATION ApplicationMetadataMessage_Type = 5 +) + +var ApplicationMetadataMessage_Type_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CHAT_MESSAGE", + 2: "CONTACT_REQUEST", + 3: "MEMBERSHIP_UPDATE_MESSAGE", + 4: "PAIR_INSTALLATION", + 5: "SYNC_INSTALLATION", +} + +var ApplicationMetadataMessage_Type_value = map[string]int32{ + "UNKNOWN": 0, + "CHAT_MESSAGE": 1, + "CONTACT_REQUEST": 2, + "MEMBERSHIP_UPDATE_MESSAGE": 3, + "PAIR_INSTALLATION": 4, + "SYNC_INSTALLATION": 5, +} + +func (x ApplicationMetadataMessage_Type) String() string { + return proto.EnumName(ApplicationMetadataMessage_Type_name, int32(x)) +} + +func (ApplicationMetadataMessage_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ad09a6406fcf24c7, []int{0, 0} +} + +type ApplicationMetadataMessage struct { + // Signature of the payload field + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + // This is the encoded protobuf of the application level message, i.e ChatMessage + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + // The type of protobuf message sent + Type ApplicationMetadataMessage_Type `protobuf:"varint,3,opt,name=type,proto3,enum=protobuf.ApplicationMetadataMessage_Type" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ApplicationMetadataMessage) Reset() { *m = ApplicationMetadataMessage{} } +func (m *ApplicationMetadataMessage) String() string { return proto.CompactTextString(m) } +func (*ApplicationMetadataMessage) ProtoMessage() {} +func (*ApplicationMetadataMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_ad09a6406fcf24c7, []int{0} +} + +func (m *ApplicationMetadataMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ApplicationMetadataMessage.Unmarshal(m, b) +} +func (m *ApplicationMetadataMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ApplicationMetadataMessage.Marshal(b, m, deterministic) +} +func (m *ApplicationMetadataMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplicationMetadataMessage.Merge(m, src) +} +func (m *ApplicationMetadataMessage) XXX_Size() int { + return xxx_messageInfo_ApplicationMetadataMessage.Size(m) +} +func (m *ApplicationMetadataMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ApplicationMetadataMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_ApplicationMetadataMessage proto.InternalMessageInfo + +func (m *ApplicationMetadataMessage) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *ApplicationMetadataMessage) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *ApplicationMetadataMessage) GetType() ApplicationMetadataMessage_Type { + if m != nil { + return m.Type + } + return ApplicationMetadataMessage_UNKNOWN +} + +func init() { + proto.RegisterEnum("protobuf.ApplicationMetadataMessage_Type", ApplicationMetadataMessage_Type_name, ApplicationMetadataMessage_Type_value) + proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") +} + +func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) } + +var fileDescriptor_ad09a6406fcf24c7 = []byte{ + // 269 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x8e, 0x41, 0x4b, 0xc3, 0x30, + 0x18, 0x86, 0x6d, 0x57, 0x9d, 0x7e, 0x0e, 0xad, 0x11, 0xa1, 0x8a, 0xc2, 0xe8, 0x69, 0x5e, 0x7a, + 0xd0, 0xb3, 0x87, 0x58, 0x83, 0x2b, 0xae, 0x69, 0x4d, 0x52, 0xc4, 0x53, 0xc8, 0x5c, 0x1c, 0x85, + 0x6d, 0x0d, 0x6b, 0x76, 0xe8, 0x2f, 0xf0, 0x57, 0xf8, 0x5f, 0x65, 0xd5, 0x39, 0x3c, 0x78, 0x0a, + 0xef, 0x93, 0xf7, 0xe1, 0xfd, 0x20, 0x54, 0xc6, 0xcc, 0xca, 0x37, 0x65, 0xcb, 0x6a, 0x21, 0xe7, + 0xda, 0xaa, 0x89, 0xb2, 0x4a, 0xce, 0x75, 0x5d, 0xab, 0xa9, 0x8e, 0xcc, 0xb2, 0xb2, 0x15, 0xda, + 0x6f, 0x9f, 0xf1, 0xea, 0x3d, 0xfc, 0x74, 0xe1, 0x02, 0x6f, 0x85, 0xf4, 0xa7, 0x9f, 0x7e, 0xd7, + 0xd1, 0x25, 0x1c, 0xd4, 0xe5, 0x74, 0xa1, 0xec, 0x6a, 0xa9, 0x03, 0xa7, 0xef, 0x0c, 0x7a, 0x6c, + 0x0b, 0x50, 0x00, 0x5d, 0xa3, 0x9a, 0x59, 0xa5, 0x26, 0x81, 0xdb, 0xfe, 0x6d, 0x22, 0xba, 0x03, + 0xcf, 0x36, 0x46, 0x07, 0x9d, 0xbe, 0x33, 0x38, 0xba, 0xb9, 0x8e, 0x36, 0x7b, 0xd1, 0xff, 0x5b, + 0x91, 0x68, 0x8c, 0x66, 0xad, 0x16, 0x7e, 0x38, 0xe0, 0xad, 0x23, 0x3a, 0x84, 0x6e, 0x41, 0x9f, + 0x68, 0xf6, 0x42, 0xfd, 0x1d, 0xe4, 0x43, 0x2f, 0x1e, 0x62, 0x21, 0x53, 0xc2, 0x39, 0x7e, 0x24, + 0xbe, 0x83, 0x4e, 0xe1, 0x38, 0xce, 0xa8, 0xc0, 0xb1, 0x90, 0x8c, 0x3c, 0x17, 0x84, 0x0b, 0xdf, + 0x45, 0x57, 0x70, 0x9e, 0x92, 0xf4, 0x9e, 0x30, 0x3e, 0x4c, 0x72, 0x59, 0xe4, 0x0f, 0x58, 0x90, + 0x5f, 0xa7, 0x83, 0xce, 0xe0, 0x24, 0xc7, 0x09, 0x93, 0x09, 0xe5, 0x02, 0x8f, 0x46, 0x58, 0x24, + 0x19, 0xf5, 0xbd, 0x35, 0xe6, 0xaf, 0x34, 0xfe, 0x8b, 0x77, 0xc7, 0x7b, 0xed, 0xe5, 0xb7, 0x5f, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xb9, 0xdb, 0x0d, 0x56, 0x56, 0x01, 0x00, 0x00, +} diff --git a/protocol/protobuf/application_metadata_message.proto b/protocol/protobuf/application_metadata_message.proto new file mode 100644 index 000000000..9d99259ca --- /dev/null +++ b/protocol/protobuf/application_metadata_message.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package protobuf; + +message ApplicationMetadataMessage { + // Signature of the payload field + bytes signature = 1; + // This is the encoded protobuf of the application level message, i.e ChatMessage + bytes payload = 2; + + // The type of protobuf message sent + Type type = 3; + + enum Type { + UNKNOWN = 0; + CHAT_MESSAGE = 1; + CONTACT_REQUEST = 2; + MEMBERSHIP_UPDATE_MESSAGE = 3; + PAIR_INSTALLATION = 4; + SYNC_INSTALLATION = 5; + } +} diff --git a/protocol/protobuf/chat_message.pb.go b/protocol/protobuf/chat_message.pb.go new file mode 100644 index 000000000..25250309a --- /dev/null +++ b/protocol/protobuf/chat_message.pb.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: chat_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type ChatMessage_MessageType int32 + +const ( + ChatMessage_UNKNOWN_MESSAGE_TYPE ChatMessage_MessageType = 0 + ChatMessage_ONE_TO_ONE ChatMessage_MessageType = 1 + ChatMessage_PUBLIC_GROUP ChatMessage_MessageType = 2 + ChatMessage_PRIVATE_GROUP ChatMessage_MessageType = 3 + // Only local + ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP ChatMessage_MessageType = 4 +) + +var ChatMessage_MessageType_name = map[int32]string{ + 0: "UNKNOWN_MESSAGE_TYPE", + 1: "ONE_TO_ONE", + 2: "PUBLIC_GROUP", + 3: "PRIVATE_GROUP", + 4: "SYSTEM_MESSAGE_PRIVATE_GROUP", +} + +var ChatMessage_MessageType_value = map[string]int32{ + "UNKNOWN_MESSAGE_TYPE": 0, + "ONE_TO_ONE": 1, + "PUBLIC_GROUP": 2, + "PRIVATE_GROUP": 3, + "SYSTEM_MESSAGE_PRIVATE_GROUP": 4, +} + +func (x ChatMessage_MessageType) String() string { + return proto.EnumName(ChatMessage_MessageType_name, int32(x)) +} + +func (ChatMessage_MessageType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1, 0} +} + +type ChatMessage_ContentType int32 + +const ( + ChatMessage_UNKNOWN_CONTENT_TYPE ChatMessage_ContentType = 0 + ChatMessage_TEXT_PLAIN ChatMessage_ContentType = 1 + ChatMessage_STICKER ChatMessage_ContentType = 2 + ChatMessage_STATUS ChatMessage_ContentType = 3 + ChatMessage_EMOJI ChatMessage_ContentType = 4 + ChatMessage_COMMAND ChatMessage_ContentType = 5 + ChatMessage_COMMAND_REQUEST ChatMessage_ContentType = 6 +) + +var ChatMessage_ContentType_name = map[int32]string{ + 0: "UNKNOWN_CONTENT_TYPE", + 1: "TEXT_PLAIN", + 2: "STICKER", + 3: "STATUS", + 4: "EMOJI", + 5: "COMMAND", + 6: "COMMAND_REQUEST", +} + +var ChatMessage_ContentType_value = map[string]int32{ + "UNKNOWN_CONTENT_TYPE": 0, + "TEXT_PLAIN": 1, + "STICKER": 2, + "STATUS": 3, + "EMOJI": 4, + "COMMAND": 5, + "COMMAND_REQUEST": 6, +} + +func (x ChatMessage_ContentType) String() string { + return proto.EnumName(ChatMessage_ContentType_name, int32(x)) +} + +func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1, 1} +} + +type StickerMessage struct { + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Pack int32 `protobuf:"varint,2,opt,name=pack,proto3" json:"pack,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StickerMessage) Reset() { *m = StickerMessage{} } +func (m *StickerMessage) String() string { return proto.CompactTextString(m) } +func (*StickerMessage) ProtoMessage() {} +func (*StickerMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{0} +} + +func (m *StickerMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StickerMessage.Unmarshal(m, b) +} +func (m *StickerMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StickerMessage.Marshal(b, m, deterministic) +} +func (m *StickerMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_StickerMessage.Merge(m, src) +} +func (m *StickerMessage) XXX_Size() int { + return xxx_messageInfo_StickerMessage.Size(m) +} +func (m *StickerMessage) XXX_DiscardUnknown() { + xxx_messageInfo_StickerMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_StickerMessage proto.InternalMessageInfo + +func (m *StickerMessage) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +func (m *StickerMessage) GetPack() int32 { + if m != nil { + return m.Pack + } + return 0 +} + +type ChatMessage struct { + // Lamport timestamp of the chat message + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + // Unix timestamps in milliseconds, currently not used as we use whisper as more reliable, but here + // so that we don't rely on it + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Text of the message + Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"` + // Id of the message that we are replying to + ResponseTo string `protobuf:"bytes,4,opt,name=response_to,json=responseTo,proto3" json:"response_to,omitempty"` + // Ens name of the sender + EnsName string `protobuf:"bytes,5,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` + // Chat id, this field is symmetric for public-chats and private group chats, + // but asymmetric in case of one-to-ones, as the sender will use the chat-id + // of the received, while the receiver will use the chat-id of the sender. + // Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order + ChatId string `protobuf:"bytes,6,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // The type of message (public/one-to-one/private-group-chat) + MessageType ChatMessage_MessageType `protobuf:"varint,7,opt,name=message_type,json=messageType,proto3,enum=protobuf.ChatMessage_MessageType" json:"message_type,omitempty"` + // The type of the content of the message + ContentType ChatMessage_ContentType `protobuf:"varint,8,opt,name=content_type,json=contentType,proto3,enum=protobuf.ChatMessage_ContentType" json:"content_type,omitempty"` + // Types that are valid to be assigned to Payload: + // *ChatMessage_Sticker + Payload isChatMessage_Payload `protobuf_oneof:"payload"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatMessage) Reset() { *m = ChatMessage{} } +func (m *ChatMessage) String() string { return proto.CompactTextString(m) } +func (*ChatMessage) ProtoMessage() {} +func (*ChatMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1} +} + +func (m *ChatMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatMessage.Unmarshal(m, b) +} +func (m *ChatMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatMessage.Marshal(b, m, deterministic) +} +func (m *ChatMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatMessage.Merge(m, src) +} +func (m *ChatMessage) XXX_Size() int { + return xxx_messageInfo_ChatMessage.Size(m) +} +func (m *ChatMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ChatMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatMessage proto.InternalMessageInfo + +func (m *ChatMessage) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + +func (m *ChatMessage) GetTimestamp() uint64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *ChatMessage) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +func (m *ChatMessage) GetResponseTo() string { + if m != nil { + return m.ResponseTo + } + return "" +} + +func (m *ChatMessage) GetEnsName() string { + if m != nil { + return m.EnsName + } + return "" +} + +func (m *ChatMessage) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *ChatMessage) GetMessageType() ChatMessage_MessageType { + if m != nil { + return m.MessageType + } + return ChatMessage_UNKNOWN_MESSAGE_TYPE +} + +func (m *ChatMessage) GetContentType() ChatMessage_ContentType { + if m != nil { + return m.ContentType + } + return ChatMessage_UNKNOWN_CONTENT_TYPE +} + +type isChatMessage_Payload interface { + isChatMessage_Payload() +} + +type ChatMessage_Sticker struct { + Sticker *StickerMessage `protobuf:"bytes,9,opt,name=sticker,proto3,oneof"` +} + +func (*ChatMessage_Sticker) isChatMessage_Payload() {} + +func (m *ChatMessage) GetPayload() isChatMessage_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (m *ChatMessage) GetSticker() *StickerMessage { + if x, ok := m.GetPayload().(*ChatMessage_Sticker); ok { + return x.Sticker + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ChatMessage) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ChatMessage_Sticker)(nil), + } +} + +func init() { + proto.RegisterEnum("protobuf.ChatMessage_MessageType", ChatMessage_MessageType_name, ChatMessage_MessageType_value) + proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value) + proto.RegisterType((*StickerMessage)(nil), "protobuf.StickerMessage") + proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage") +} + +func init() { proto.RegisterFile("chat_message.proto", fileDescriptor_263952f55fd35689) } + +var fileDescriptor_263952f55fd35689 = []byte{ + // 462 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0xcf, 0x6f, 0xd3, 0x30, + 0x14, 0x5e, 0xda, 0xb4, 0x69, 0x5f, 0x46, 0x31, 0x66, 0x12, 0x46, 0x9a, 0x44, 0xe9, 0xa9, 0xa7, + 0x1e, 0x06, 0x07, 0xae, 0x5d, 0x66, 0x8d, 0xb0, 0xc5, 0x29, 0x8e, 0x0b, 0xec, 0x64, 0x79, 0xa9, + 0xa1, 0x55, 0x97, 0x1f, 0x6a, 0x8c, 0x44, 0x0f, 0xf0, 0x67, 0x73, 0x46, 0x71, 0x5a, 0xda, 0x5d, + 0x76, 0xf2, 0xfb, 0xbe, 0xf7, 0xbd, 0xef, 0xe5, 0x7d, 0x01, 0x9c, 0x2e, 0x95, 0x91, 0x99, 0xae, + 0x2a, 0xf5, 0x43, 0x4f, 0xca, 0x4d, 0x61, 0x0a, 0xdc, 0xb3, 0xcf, 0xfd, 0xcf, 0xef, 0xa3, 0x0f, + 0x30, 0x48, 0xcc, 0x2a, 0x5d, 0xeb, 0x4d, 0xd4, 0x28, 0x30, 0x06, 0x77, 0xa9, 0xaa, 0x25, 0x71, + 0x86, 0xce, 0xb8, 0xcf, 0x6d, 0x5d, 0x73, 0xa5, 0x4a, 0xd7, 0xa4, 0x35, 0x74, 0xc6, 0x1d, 0x6e, + 0xeb, 0xd1, 0x5f, 0x17, 0xfc, 0x60, 0xa9, 0xcc, 0x7e, 0xee, 0x0c, 0x3a, 0xe9, 0x43, 0x91, 0xae, + 0xed, 0xa0, 0xcb, 0x1b, 0x80, 0xcf, 0xa1, 0x6f, 0x56, 0x99, 0xae, 0x8c, 0xca, 0x4a, 0x3b, 0xee, + 0xf2, 0x03, 0x51, 0xfb, 0x1a, 0xfd, 0xcb, 0x90, 0x76, 0xb3, 0xab, 0xae, 0xf1, 0x1b, 0xf0, 0x37, + 0xba, 0x2a, 0x8b, 0xbc, 0xd2, 0xd2, 0x14, 0xc4, 0xb5, 0x2d, 0xd8, 0x53, 0xa2, 0xc0, 0xaf, 0xa1, + 0xa7, 0xf3, 0x4a, 0xe6, 0x2a, 0xd3, 0xa4, 0x63, 0xbb, 0x9e, 0xce, 0x2b, 0xa6, 0x32, 0x8d, 0x5f, + 0x81, 0x67, 0xaf, 0x5d, 0x2d, 0x48, 0xd7, 0x76, 0xba, 0x35, 0x0c, 0x17, 0xf8, 0x0a, 0x4e, 0x77, + 0x09, 0x48, 0xb3, 0x2d, 0x35, 0xf1, 0x86, 0xce, 0x78, 0x70, 0xf1, 0x76, 0xb2, 0xcf, 0x61, 0x72, + 0x74, 0xc9, 0x64, 0xf7, 0x8a, 0x6d, 0xa9, 0xb9, 0x9f, 0x1d, 0x40, 0xed, 0x92, 0x16, 0xb9, 0xd1, + 0xb9, 0x69, 0x5c, 0x7a, 0x4f, 0xb9, 0x04, 0x8d, 0xb2, 0x71, 0x49, 0x0f, 0x00, 0xbf, 0x07, 0xaf, + 0x6a, 0x22, 0x27, 0xfd, 0xa1, 0x33, 0xf6, 0x2f, 0xc8, 0xc1, 0xe0, 0xf1, 0xbf, 0xf8, 0x78, 0xc2, + 0xf7, 0xd2, 0xd1, 0x1f, 0xf0, 0x8f, 0xbe, 0x0b, 0x13, 0x38, 0x9b, 0xb3, 0x1b, 0x16, 0x7f, 0x65, + 0x32, 0xa2, 0x49, 0x32, 0xbd, 0xa6, 0x52, 0xdc, 0xcd, 0x28, 0x3a, 0xc1, 0x03, 0x80, 0x98, 0x51, + 0x29, 0x62, 0x19, 0x33, 0x8a, 0x1c, 0x8c, 0xe0, 0x74, 0x36, 0xbf, 0xbc, 0x0d, 0x03, 0x79, 0xcd, + 0xe3, 0xf9, 0x0c, 0xb5, 0xf0, 0x0b, 0x78, 0x36, 0xe3, 0xe1, 0x97, 0xa9, 0xa0, 0x3b, 0xaa, 0x8d, + 0x87, 0x70, 0x9e, 0xdc, 0x25, 0x82, 0x46, 0xff, 0xdd, 0x1e, 0x2b, 0xdc, 0xd1, 0x6f, 0xf0, 0x8f, + 0x2e, 0x3a, 0xde, 0x1f, 0xc4, 0x4c, 0x50, 0x26, 0x8e, 0xf6, 0x0b, 0xfa, 0x4d, 0xc8, 0xd9, 0xed, + 0x34, 0x64, 0xc8, 0xc1, 0x3e, 0x78, 0x89, 0x08, 0x83, 0x1b, 0xca, 0x51, 0x0b, 0x03, 0x74, 0x13, + 0x31, 0x15, 0xf3, 0x04, 0xb5, 0x71, 0x1f, 0x3a, 0x34, 0x8a, 0x3f, 0x85, 0xc8, 0xad, 0x35, 0x41, + 0x1c, 0x45, 0x53, 0x76, 0x85, 0x3a, 0xf8, 0x25, 0x3c, 0xdf, 0x01, 0xc9, 0xe9, 0xe7, 0x39, 0x4d, + 0x04, 0xea, 0x5e, 0xf6, 0xc1, 0x2b, 0xd5, 0xf6, 0xa1, 0x50, 0x8b, 0xfb, 0xae, 0x4d, 0xeb, 0xdd, + 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x19, 0x70, 0x9c, 0xd9, 0x02, 0x00, 0x00, +} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/message.proto b/protocol/protobuf/chat_message.proto similarity index 64% rename from vendor/github.com/status-im/status-go/protocol/protobuf/message.proto rename to protocol/protobuf/chat_message.proto index ec773b99c..e7d2309da 100644 --- a/vendor/github.com/status-im/status-go/protocol/protobuf/message.proto +++ b/protocol/protobuf/chat_message.proto @@ -35,36 +35,20 @@ message ChatMessage { } enum MessageType { - ONE_TO_ONE = 0; - PUBLIC_GROUP = 1; - PRIVATE_GROUP = 2; + UNKNOWN_MESSAGE_TYPE = 0; + ONE_TO_ONE = 1; + PUBLIC_GROUP = 2; + PRIVATE_GROUP = 3; // Only local - SYSTEM_MESSAGE_PRIVATE_GROUP = 3; + SYSTEM_MESSAGE_PRIVATE_GROUP = 4; } enum ContentType { - TEXT_PLAIN = 0; - STICKER = 1; - STATUS = 2; - EMOJI = 3; - COMMAND = 4; - COMMAND_REQUEST = 5; - } -} - -message ApplicationMetadataMessage { - // Signature of the payload field - bytes signature = 1; - // This is the encoded protobuf of the application level message, i.e ChatMessage - bytes payload = 2; - - // The type of protobuf message sent - MessageType message_type = 3; - - enum MessageType { - TEXT_MESSAGE = 0; - CONTACT_REQUEST = 1; - MEMBERSHIP_UPDATE = 2; - PAIR_INSTALLATION = 3; - SYNC_INSTALLATION = 4; + UNKNOWN_CONTENT_TYPE = 0; + TEXT_PLAIN = 1; + STICKER = 2; + STATUS = 3; + EMOJI = 4; + COMMAND = 5; + COMMAND_REQUEST = 6; } } diff --git a/protocol/protobuf/membership_update_message.pb.go b/protocol/protobuf/membership_update_message.pb.go new file mode 100644 index 000000000..30dd3d1f2 --- /dev/null +++ b/protocol/protobuf/membership_update_message.pb.go @@ -0,0 +1,227 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: membership_update_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type MembershipUpdateEvent_EventType int32 + +const ( + MembershipUpdateEvent_UNKNOWN MembershipUpdateEvent_EventType = 0 + MembershipUpdateEvent_CHAT_CREATED MembershipUpdateEvent_EventType = 1 + MembershipUpdateEvent_NAME_CHANGED MembershipUpdateEvent_EventType = 2 + MembershipUpdateEvent_MEMBERS_ADDED MembershipUpdateEvent_EventType = 3 + MembershipUpdateEvent_MEMBER_JOINED MembershipUpdateEvent_EventType = 4 + MembershipUpdateEvent_MEMBER_REMOVED MembershipUpdateEvent_EventType = 5 + MembershipUpdateEvent_ADMINS_ADDED MembershipUpdateEvent_EventType = 6 + MembershipUpdateEvent_ADMIN_REMOVED MembershipUpdateEvent_EventType = 7 +) + +var MembershipUpdateEvent_EventType_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CHAT_CREATED", + 2: "NAME_CHANGED", + 3: "MEMBERS_ADDED", + 4: "MEMBER_JOINED", + 5: "MEMBER_REMOVED", + 6: "ADMINS_ADDED", + 7: "ADMIN_REMOVED", +} + +var MembershipUpdateEvent_EventType_value = map[string]int32{ + "UNKNOWN": 0, + "CHAT_CREATED": 1, + "NAME_CHANGED": 2, + "MEMBERS_ADDED": 3, + "MEMBER_JOINED": 4, + "MEMBER_REMOVED": 5, + "ADMINS_ADDED": 6, + "ADMIN_REMOVED": 7, +} + +func (x MembershipUpdateEvent_EventType) String() string { + return proto.EnumName(MembershipUpdateEvent_EventType_name, int32(x)) +} + +func (MembershipUpdateEvent_EventType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{0, 0} +} + +type MembershipUpdateEvent struct { + // Lamport timestamp of the event + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + // List of public keys of objects of the action + Members []string `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"` + // Name of the chat for the CHAT_CREATED/NAME_CHANGED event types + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + // The type of the event + Type MembershipUpdateEvent_EventType `protobuf:"varint,4,opt,name=type,proto3,enum=protobuf.MembershipUpdateEvent_EventType" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MembershipUpdateEvent) Reset() { *m = MembershipUpdateEvent{} } +func (m *MembershipUpdateEvent) String() string { return proto.CompactTextString(m) } +func (*MembershipUpdateEvent) ProtoMessage() {} +func (*MembershipUpdateEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{0} +} + +func (m *MembershipUpdateEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MembershipUpdateEvent.Unmarshal(m, b) +} +func (m *MembershipUpdateEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MembershipUpdateEvent.Marshal(b, m, deterministic) +} +func (m *MembershipUpdateEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_MembershipUpdateEvent.Merge(m, src) +} +func (m *MembershipUpdateEvent) XXX_Size() int { + return xxx_messageInfo_MembershipUpdateEvent.Size(m) +} +func (m *MembershipUpdateEvent) XXX_DiscardUnknown() { + xxx_messageInfo_MembershipUpdateEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_MembershipUpdateEvent proto.InternalMessageInfo + +func (m *MembershipUpdateEvent) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + +func (m *MembershipUpdateEvent) GetMembers() []string { + if m != nil { + return m.Members + } + return nil +} + +func (m *MembershipUpdateEvent) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MembershipUpdateEvent) GetType() MembershipUpdateEvent_EventType { + if m != nil { + return m.Type + } + return MembershipUpdateEvent_UNKNOWN +} + +// MembershipUpdateMessage is a message used to propagate information +// about group membership changes. +// For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. +type MembershipUpdateMessage struct { + // The chat id of the private group chat + ChatId string `protobuf:"bytes,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // A list of events for this group chat, first x bytes are the signature, then is a + // protobuf encoded MembershipUpdateEvent + Events [][]byte `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` + // An optional chat message + Message *ChatMessage `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MembershipUpdateMessage) Reset() { *m = MembershipUpdateMessage{} } +func (m *MembershipUpdateMessage) String() string { return proto.CompactTextString(m) } +func (*MembershipUpdateMessage) ProtoMessage() {} +func (*MembershipUpdateMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{1} +} + +func (m *MembershipUpdateMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MembershipUpdateMessage.Unmarshal(m, b) +} +func (m *MembershipUpdateMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MembershipUpdateMessage.Marshal(b, m, deterministic) +} +func (m *MembershipUpdateMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_MembershipUpdateMessage.Merge(m, src) +} +func (m *MembershipUpdateMessage) XXX_Size() int { + return xxx_messageInfo_MembershipUpdateMessage.Size(m) +} +func (m *MembershipUpdateMessage) XXX_DiscardUnknown() { + xxx_messageInfo_MembershipUpdateMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_MembershipUpdateMessage proto.InternalMessageInfo + +func (m *MembershipUpdateMessage) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *MembershipUpdateMessage) GetEvents() [][]byte { + if m != nil { + return m.Events + } + return nil +} + +func (m *MembershipUpdateMessage) GetMessage() *ChatMessage { + if m != nil { + return m.Message + } + return nil +} + +func init() { + proto.RegisterEnum("protobuf.MembershipUpdateEvent_EventType", MembershipUpdateEvent_EventType_name, MembershipUpdateEvent_EventType_value) + proto.RegisterType((*MembershipUpdateEvent)(nil), "protobuf.MembershipUpdateEvent") + proto.RegisterType((*MembershipUpdateMessage)(nil), "protobuf.MembershipUpdateMessage") +} + +func init() { proto.RegisterFile("membership_update_message.proto", fileDescriptor_8d37dd0dc857a6be) } + +var fileDescriptor_8d37dd0dc857a6be = []byte{ + // 340 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x41, 0x4f, 0xf2, 0x40, + 0x10, 0xfd, 0x0a, 0xa5, 0xfd, 0x3a, 0x20, 0xa9, 0x13, 0x91, 0xc6, 0x8b, 0x0d, 0xa7, 0x7a, 0xa9, + 0x09, 0x9e, 0x3d, 0xd4, 0xee, 0x46, 0xd0, 0xec, 0x92, 0xac, 0xa0, 0xc7, 0xa6, 0xc0, 0x2a, 0x44, + 0x0b, 0x0d, 0x14, 0x13, 0xe2, 0x4f, 0xf1, 0x8f, 0xf8, 0xf3, 0x4c, 0x97, 0x16, 0xa2, 0xf1, 0xb2, + 0xbb, 0xef, 0xcd, 0xbc, 0x37, 0x3b, 0x0f, 0xce, 0x13, 0x99, 0x8c, 0xe5, 0x6a, 0x3d, 0x9b, 0xa7, + 0xd1, 0x26, 0x9d, 0xc6, 0x99, 0x8c, 0x12, 0xb9, 0x5e, 0xc7, 0x2f, 0xd2, 0x4f, 0x57, 0xcb, 0x6c, + 0x89, 0xff, 0xd5, 0x35, 0xde, 0x3c, 0x9f, 0xe1, 0x64, 0x16, 0x67, 0x3f, 0xab, 0x9d, 0xaf, 0x0a, + 0xb4, 0xd8, 0xde, 0x61, 0xa4, 0x0c, 0xe8, 0xbb, 0x5c, 0x64, 0x78, 0x02, 0xb5, 0xc9, 0xdb, 0x72, + 0xf2, 0xea, 0x68, 0xae, 0xe6, 0xe9, 0x62, 0x07, 0xd0, 0x01, 0xb3, 0x18, 0xe8, 0x54, 0xdc, 0xaa, + 0x67, 0x89, 0x12, 0x22, 0x82, 0xbe, 0x88, 0x13, 0xe9, 0x54, 0x5d, 0xcd, 0xb3, 0x84, 0x7a, 0xe3, + 0x35, 0xe8, 0xd9, 0x36, 0x95, 0x8e, 0xee, 0x6a, 0x5e, 0xb3, 0x7b, 0xe1, 0x97, 0x5f, 0xf1, 0xff, + 0x1c, 0xe9, 0xab, 0x73, 0xb8, 0x4d, 0xa5, 0x50, 0xb2, 0xce, 0xa7, 0x06, 0xd6, 0x9e, 0xc3, 0x3a, + 0x98, 0x23, 0x7e, 0xcf, 0x07, 0x4f, 0xdc, 0xfe, 0x87, 0x36, 0x34, 0xc2, 0x5e, 0x30, 0x8c, 0x42, + 0x41, 0x83, 0x21, 0x25, 0xb6, 0x96, 0x33, 0x3c, 0x60, 0x34, 0x0a, 0x7b, 0x01, 0xbf, 0xa5, 0xc4, + 0xae, 0xe0, 0x31, 0x1c, 0x31, 0xca, 0x6e, 0xa8, 0x78, 0x88, 0x02, 0x42, 0x28, 0xb1, 0xab, 0x07, + 0x2a, 0xba, 0x1b, 0xf4, 0x39, 0x25, 0xb6, 0x8e, 0x08, 0xcd, 0x82, 0x12, 0x94, 0x0d, 0x1e, 0x29, + 0xb1, 0x6b, 0xb9, 0x57, 0x40, 0x58, 0x9f, 0x97, 0x42, 0x23, 0x17, 0x2a, 0x66, 0xdf, 0x64, 0x76, + 0x3e, 0xa0, 0xfd, 0x7b, 0x0d, 0xb6, 0xcb, 0x16, 0xdb, 0x60, 0xaa, 0xac, 0xe7, 0x53, 0x95, 0x9e, + 0x25, 0x8c, 0x1c, 0xf6, 0xa7, 0x78, 0x0a, 0x86, 0xcc, 0x17, 0xda, 0xa5, 0xd7, 0x10, 0x05, 0xc2, + 0xcb, 0x3c, 0x56, 0xa5, 0x55, 0xf9, 0xd5, 0xbb, 0xad, 0x43, 0x56, 0xe1, 0x2c, 0xce, 0x0a, 0x63, + 0x51, 0x76, 0x8d, 0x0d, 0x55, 0xbe, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x7f, 0xb8, 0xe8, 0x9e, + 0xff, 0x01, 0x00, 0x00, +} diff --git a/protocol/protobuf/membership_update_message.proto b/protocol/protobuf/membership_update_message.proto new file mode 100644 index 000000000..8831b3f3c --- /dev/null +++ b/protocol/protobuf/membership_update_message.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package protobuf; + +import "chat_message.proto"; + +message MembershipUpdateEvent { + // Lamport timestamp of the event + uint64 clock = 1; + // List of public keys of objects of the action + repeated string members = 2; + // Name of the chat for the CHAT_CREATED/NAME_CHANGED event types + string name = 3; + // The type of the event + EventType type = 4; + + enum EventType { + UNKNOWN = 0; + CHAT_CREATED = 1; + NAME_CHANGED = 2; + MEMBERS_ADDED = 3; + MEMBER_JOINED = 4; + MEMBER_REMOVED = 5; + ADMINS_ADDED = 6; + ADMIN_REMOVED = 7; + } +} + +// MembershipUpdateMessage is a message used to propagate information +// about group membership changes. +// For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. +message MembershipUpdateMessage { + // The chat id of the private group chat + string chat_id = 1; + // A list of events for this group chat, first x bytes are the signature, then is a + // protobuf encoded MembershipUpdateEvent + repeated bytes events = 2; + // An optional chat message + ChatMessage message = 3; +} diff --git a/protocol/protobuf/message.pb.go b/protocol/protobuf/message.pb.go deleted file mode 100644 index f3313c134..000000000 --- a/protocol/protobuf/message.pb.go +++ /dev/null @@ -1,421 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: message.proto - -package protobuf - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ChatMessage_MessageType int32 - -const ( - ChatMessage_ONE_TO_ONE ChatMessage_MessageType = 0 - ChatMessage_PUBLIC_GROUP ChatMessage_MessageType = 1 - ChatMessage_PRIVATE_GROUP ChatMessage_MessageType = 2 - // Only local - ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP ChatMessage_MessageType = 3 -) - -var ChatMessage_MessageType_name = map[int32]string{ - 0: "ONE_TO_ONE", - 1: "PUBLIC_GROUP", - 2: "PRIVATE_GROUP", - 3: "SYSTEM_MESSAGE_PRIVATE_GROUP", -} - -var ChatMessage_MessageType_value = map[string]int32{ - "ONE_TO_ONE": 0, - "PUBLIC_GROUP": 1, - "PRIVATE_GROUP": 2, - "SYSTEM_MESSAGE_PRIVATE_GROUP": 3, -} - -func (x ChatMessage_MessageType) String() string { - return proto.EnumName(ChatMessage_MessageType_name, int32(x)) -} - -func (ChatMessage_MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1, 0} -} - -type ChatMessage_ContentType int32 - -const ( - ChatMessage_TEXT_PLAIN ChatMessage_ContentType = 0 - ChatMessage_STICKER ChatMessage_ContentType = 1 - ChatMessage_STATUS ChatMessage_ContentType = 2 - ChatMessage_EMOJI ChatMessage_ContentType = 3 - ChatMessage_COMMAND ChatMessage_ContentType = 4 - ChatMessage_COMMAND_REQUEST ChatMessage_ContentType = 5 -) - -var ChatMessage_ContentType_name = map[int32]string{ - 0: "TEXT_PLAIN", - 1: "STICKER", - 2: "STATUS", - 3: "EMOJI", - 4: "COMMAND", - 5: "COMMAND_REQUEST", -} - -var ChatMessage_ContentType_value = map[string]int32{ - "TEXT_PLAIN": 0, - "STICKER": 1, - "STATUS": 2, - "EMOJI": 3, - "COMMAND": 4, - "COMMAND_REQUEST": 5, -} - -func (x ChatMessage_ContentType) String() string { - return proto.EnumName(ChatMessage_ContentType_name, int32(x)) -} - -func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1, 1} -} - -type ApplicationMetadataMessage_MessageType int32 - -const ( - ApplicationMetadataMessage_TEXT_MESSAGE ApplicationMetadataMessage_MessageType = 0 - ApplicationMetadataMessage_CONTACT_REQUEST ApplicationMetadataMessage_MessageType = 1 - ApplicationMetadataMessage_MEMBERSHIP_UPDATE ApplicationMetadataMessage_MessageType = 2 - ApplicationMetadataMessage_PAIR_INSTALLATION ApplicationMetadataMessage_MessageType = 3 - ApplicationMetadataMessage_SYNC_INSTALLATION ApplicationMetadataMessage_MessageType = 4 -) - -var ApplicationMetadataMessage_MessageType_name = map[int32]string{ - 0: "TEXT_MESSAGE", - 1: "CONTACT_REQUEST", - 2: "MEMBERSHIP_UPDATE", - 3: "PAIR_INSTALLATION", - 4: "SYNC_INSTALLATION", -} - -var ApplicationMetadataMessage_MessageType_value = map[string]int32{ - "TEXT_MESSAGE": 0, - "CONTACT_REQUEST": 1, - "MEMBERSHIP_UPDATE": 2, - "PAIR_INSTALLATION": 3, - "SYNC_INSTALLATION": 4, -} - -func (x ApplicationMetadataMessage_MessageType) String() string { - return proto.EnumName(ApplicationMetadataMessage_MessageType_name, int32(x)) -} - -func (ApplicationMetadataMessage_MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{2, 0} -} - -type StickerMessage struct { - Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` - Pack int32 `protobuf:"varint,2,opt,name=pack,proto3" json:"pack,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StickerMessage) Reset() { *m = StickerMessage{} } -func (m *StickerMessage) String() string { return proto.CompactTextString(m) } -func (*StickerMessage) ProtoMessage() {} -func (*StickerMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{0} -} - -func (m *StickerMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_StickerMessage.Unmarshal(m, b) -} -func (m *StickerMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_StickerMessage.Marshal(b, m, deterministic) -} -func (m *StickerMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_StickerMessage.Merge(m, src) -} -func (m *StickerMessage) XXX_Size() int { - return xxx_messageInfo_StickerMessage.Size(m) -} -func (m *StickerMessage) XXX_DiscardUnknown() { - xxx_messageInfo_StickerMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_StickerMessage proto.InternalMessageInfo - -func (m *StickerMessage) GetHash() string { - if m != nil { - return m.Hash - } - return "" -} - -func (m *StickerMessage) GetPack() int32 { - if m != nil { - return m.Pack - } - return 0 -} - -type ChatMessage struct { - // Lamport timestamp of the chat message - Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` - // Unix timestamps in milliseconds, currently not used as we use whisper as more reliable, but here - // so that we don't rely on it - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // Text of the message - Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"` - // Id of the message that we are replying to - ResponseTo string `protobuf:"bytes,4,opt,name=response_to,json=responseTo,proto3" json:"response_to,omitempty"` - // Ens name of the sender - EnsName string `protobuf:"bytes,5,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` - // Chat id, this field is symmetric for public-chats and private group chats, - // but asymmetric in case of one-to-ones, as the sender will use the chat-id - // of the received, while the receiver will use the chat-id of the sender. - // Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order - ChatId string `protobuf:"bytes,6,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` - // The type of message (public/one-to-one/private-group-chat) - MessageType ChatMessage_MessageType `protobuf:"varint,7,opt,name=message_type,json=messageType,proto3,enum=protobuf.ChatMessage_MessageType" json:"message_type,omitempty"` - // The type of the content of the message - ContentType ChatMessage_ContentType `protobuf:"varint,8,opt,name=content_type,json=contentType,proto3,enum=protobuf.ChatMessage_ContentType" json:"content_type,omitempty"` - // Types that are valid to be assigned to Payload: - // *ChatMessage_Sticker - Payload isChatMessage_Payload `protobuf_oneof:"payload"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ChatMessage) Reset() { *m = ChatMessage{} } -func (m *ChatMessage) String() string { return proto.CompactTextString(m) } -func (*ChatMessage) ProtoMessage() {} -func (*ChatMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1} -} - -func (m *ChatMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ChatMessage.Unmarshal(m, b) -} -func (m *ChatMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ChatMessage.Marshal(b, m, deterministic) -} -func (m *ChatMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChatMessage.Merge(m, src) -} -func (m *ChatMessage) XXX_Size() int { - return xxx_messageInfo_ChatMessage.Size(m) -} -func (m *ChatMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ChatMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ChatMessage proto.InternalMessageInfo - -func (m *ChatMessage) GetClock() uint64 { - if m != nil { - return m.Clock - } - return 0 -} - -func (m *ChatMessage) GetTimestamp() uint64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func (m *ChatMessage) GetText() string { - if m != nil { - return m.Text - } - return "" -} - -func (m *ChatMessage) GetResponseTo() string { - if m != nil { - return m.ResponseTo - } - return "" -} - -func (m *ChatMessage) GetEnsName() string { - if m != nil { - return m.EnsName - } - return "" -} - -func (m *ChatMessage) GetChatId() string { - if m != nil { - return m.ChatId - } - return "" -} - -func (m *ChatMessage) GetMessageType() ChatMessage_MessageType { - if m != nil { - return m.MessageType - } - return ChatMessage_ONE_TO_ONE -} - -func (m *ChatMessage) GetContentType() ChatMessage_ContentType { - if m != nil { - return m.ContentType - } - return ChatMessage_TEXT_PLAIN -} - -type isChatMessage_Payload interface { - isChatMessage_Payload() -} - -type ChatMessage_Sticker struct { - Sticker *StickerMessage `protobuf:"bytes,9,opt,name=sticker,proto3,oneof"` -} - -func (*ChatMessage_Sticker) isChatMessage_Payload() {} - -func (m *ChatMessage) GetPayload() isChatMessage_Payload { - if m != nil { - return m.Payload - } - return nil -} - -func (m *ChatMessage) GetSticker() *StickerMessage { - if x, ok := m.GetPayload().(*ChatMessage_Sticker); ok { - return x.Sticker - } - return nil -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*ChatMessage) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*ChatMessage_Sticker)(nil), - } -} - -type ApplicationMetadataMessage struct { - // Signature of the payload field - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - // This is the encoded protobuf of the application level message, i.e ChatMessage - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` - // The type of protobuf message sent - MessageType ApplicationMetadataMessage_MessageType `protobuf:"varint,3,opt,name=message_type,json=messageType,proto3,enum=protobuf.ApplicationMetadataMessage_MessageType" json:"message_type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplicationMetadataMessage) Reset() { *m = ApplicationMetadataMessage{} } -func (m *ApplicationMetadataMessage) String() string { return proto.CompactTextString(m) } -func (*ApplicationMetadataMessage) ProtoMessage() {} -func (*ApplicationMetadataMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{2} -} - -func (m *ApplicationMetadataMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplicationMetadataMessage.Unmarshal(m, b) -} -func (m *ApplicationMetadataMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplicationMetadataMessage.Marshal(b, m, deterministic) -} -func (m *ApplicationMetadataMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationMetadataMessage.Merge(m, src) -} -func (m *ApplicationMetadataMessage) XXX_Size() int { - return xxx_messageInfo_ApplicationMetadataMessage.Size(m) -} -func (m *ApplicationMetadataMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationMetadataMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationMetadataMessage proto.InternalMessageInfo - -func (m *ApplicationMetadataMessage) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *ApplicationMetadataMessage) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func (m *ApplicationMetadataMessage) GetMessageType() ApplicationMetadataMessage_MessageType { - if m != nil { - return m.MessageType - } - return ApplicationMetadataMessage_TEXT_MESSAGE -} - -func init() { - proto.RegisterEnum("protobuf.ChatMessage_MessageType", ChatMessage_MessageType_name, ChatMessage_MessageType_value) - proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value) - proto.RegisterEnum("protobuf.ApplicationMetadataMessage_MessageType", ApplicationMetadataMessage_MessageType_name, ApplicationMetadataMessage_MessageType_value) - proto.RegisterType((*StickerMessage)(nil), "protobuf.StickerMessage") - proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage") - proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") -} - -func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) } - -var fileDescriptor_33c57e4bae7b9afd = []byte{ - // 563 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x5f, 0x6f, 0xda, 0x3e, - 0x14, 0x25, 0x10, 0xa0, 0xdc, 0xd0, 0xfe, 0x5c, 0xff, 0x36, 0x2d, 0x9b, 0x2a, 0x8d, 0xf1, 0xc4, - 0x13, 0x9a, 0xba, 0x3d, 0xec, 0x35, 0x4d, 0xad, 0x36, 0x1b, 0xf9, 0x33, 0xdb, 0x4c, 0xeb, 0x93, - 0xe5, 0x06, 0xaf, 0xa0, 0x96, 0x24, 0x22, 0xae, 0x34, 0xbe, 0xcf, 0x3e, 0xc9, 0x3e, 0xd9, 0x14, - 0x07, 0x0a, 0x4c, 0xea, 0x9e, 0xec, 0x73, 0x7c, 0x7d, 0xee, 0xbd, 0xe7, 0x5e, 0x38, 0x5e, 0xaa, - 0xb2, 0x94, 0x77, 0x6a, 0x5c, 0xac, 0x72, 0x9d, 0xe3, 0x23, 0x73, 0xdc, 0x3e, 0xfe, 0x18, 0x7e, - 0x82, 0x13, 0xa6, 0x17, 0xe9, 0xbd, 0x5a, 0x85, 0x75, 0x04, 0xc6, 0x60, 0xcf, 0x65, 0x39, 0x77, - 0xad, 0x81, 0x35, 0xea, 0x51, 0x73, 0xaf, 0xb8, 0x42, 0xa6, 0xf7, 0x6e, 0x73, 0x60, 0x8d, 0xda, - 0xd4, 0xdc, 0x87, 0xbf, 0x6d, 0x70, 0xfc, 0xb9, 0xd4, 0xdb, 0x7f, 0x2f, 0xa0, 0x9d, 0x3e, 0xe4, - 0xe9, 0xbd, 0xf9, 0x68, 0xd3, 0x1a, 0xe0, 0x33, 0xe8, 0xe9, 0xc5, 0x52, 0x95, 0x5a, 0x2e, 0x0b, - 0xf3, 0xdd, 0xa6, 0x3b, 0xa2, 0xd2, 0xd5, 0xea, 0xa7, 0x76, 0x5b, 0x75, 0xae, 0xea, 0x8e, 0xdf, - 0x82, 0xb3, 0x52, 0x65, 0x91, 0x67, 0xa5, 0x12, 0x3a, 0x77, 0x6d, 0xf3, 0x04, 0x5b, 0x8a, 0xe7, - 0xf8, 0x35, 0x1c, 0xa9, 0xac, 0x14, 0x99, 0x5c, 0x2a, 0xb7, 0x6d, 0x5e, 0xbb, 0x2a, 0x2b, 0x23, - 0xb9, 0x54, 0xf8, 0x15, 0x74, 0xd3, 0xb9, 0xd4, 0x62, 0x31, 0x73, 0x3b, 0xe6, 0xa5, 0x53, 0xc1, - 0x60, 0x86, 0x2f, 0xa1, 0xbf, 0x71, 0x40, 0xe8, 0x75, 0xa1, 0xdc, 0xee, 0xc0, 0x1a, 0x9d, 0x9c, - 0xbf, 0x1b, 0x6f, 0x7d, 0x18, 0xef, 0x75, 0x32, 0xde, 0x9c, 0x7c, 0x5d, 0x28, 0xea, 0x2c, 0x77, - 0xa0, 0x52, 0x49, 0xf3, 0x4c, 0xab, 0x4c, 0xd7, 0x2a, 0x47, 0xff, 0x52, 0xf1, 0xeb, 0xc8, 0x5a, - 0x25, 0xdd, 0x01, 0xfc, 0x11, 0xba, 0x65, 0x6d, 0xb9, 0xdb, 0x1b, 0x58, 0x23, 0xe7, 0xdc, 0xdd, - 0x09, 0x1c, 0xce, 0xe2, 0xba, 0x41, 0xb7, 0xa1, 0xc3, 0x19, 0x38, 0x7b, 0x75, 0xe1, 0x13, 0x80, - 0x38, 0x22, 0x82, 0xc7, 0x22, 0x8e, 0x08, 0x6a, 0x60, 0x04, 0xfd, 0x64, 0x7a, 0x31, 0x09, 0x7c, - 0x71, 0x45, 0xe3, 0x69, 0x82, 0x2c, 0x7c, 0x0a, 0xc7, 0x09, 0x0d, 0xbe, 0x79, 0x9c, 0x6c, 0xa8, - 0x26, 0x1e, 0xc0, 0x19, 0xbb, 0x61, 0x9c, 0x84, 0x22, 0x24, 0x8c, 0x79, 0x57, 0x44, 0x1c, 0x46, - 0xb4, 0x86, 0x29, 0x38, 0x7b, 0x75, 0x57, 0x59, 0x38, 0xf9, 0xce, 0x45, 0x32, 0xf1, 0x82, 0x08, - 0x35, 0xb0, 0x03, 0x5d, 0xc6, 0x03, 0xff, 0x0b, 0xa1, 0xc8, 0xc2, 0x00, 0x1d, 0xc6, 0x3d, 0x3e, - 0x65, 0xa8, 0x89, 0x7b, 0xd0, 0x26, 0x61, 0xfc, 0x39, 0x40, 0xad, 0x2a, 0xc6, 0x8f, 0xc3, 0xd0, - 0x8b, 0x2e, 0x91, 0x8d, 0xff, 0x87, 0xff, 0x36, 0x40, 0x50, 0xf2, 0x75, 0x4a, 0x18, 0x47, 0xed, - 0x8b, 0x1e, 0x74, 0x0b, 0xb9, 0x7e, 0xc8, 0xe5, 0x6c, 0xf8, 0xab, 0x09, 0x6f, 0xbc, 0xa2, 0x78, - 0x58, 0xa4, 0x52, 0x2f, 0xf2, 0x2c, 0x54, 0x5a, 0xce, 0xa4, 0x96, 0xdb, 0x9d, 0x3a, 0x83, 0x5e, - 0xb9, 0xb8, 0xcb, 0xa4, 0x7e, 0x5c, 0x29, 0xb3, 0x57, 0x7d, 0xba, 0x23, 0xb0, 0xfb, 0xa4, 0x63, - 0x36, 0xab, 0x4f, 0xb7, 0x10, 0xb3, 0xbf, 0xc6, 0xdd, 0x32, 0x83, 0x7a, 0xbf, 0xf3, 0xf9, 0xf9, - 0x9c, 0xcf, 0x4e, 0x7f, 0xb8, 0x3e, 0x9c, 0x00, 0x82, 0xbe, 0xf1, 0x66, 0x63, 0x25, 0x6a, 0xd4, - 0xcd, 0x46, 0xdc, 0xf3, 0xf9, 0x53, 0xb3, 0x16, 0x7e, 0x09, 0xa7, 0x21, 0x09, 0x2f, 0x08, 0x65, - 0xd7, 0x41, 0x22, 0xa6, 0xc9, 0xa5, 0xc7, 0x09, 0x6a, 0x56, 0x74, 0xe2, 0x05, 0x54, 0x04, 0x11, - 0xe3, 0xde, 0x64, 0xe2, 0xf1, 0x20, 0x8e, 0x50, 0xab, 0xa2, 0xd9, 0x4d, 0xe4, 0x1f, 0xd2, 0xf6, - 0x6d, 0xc7, 0x14, 0xfe, 0xe1, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf6, 0xa4, 0x11, 0xfe, 0xc7, - 0x03, 0x00, 0x00, -} diff --git a/protocol/protobuf/service.go b/protocol/protobuf/service.go index 5724ba066..56d5d54c2 100644 --- a/protocol/protobuf/service.go +++ b/protocol/protobuf/service.go @@ -4,7 +4,7 @@ import ( "github.com/golang/protobuf/proto" ) -//go:generate protoc --go_out=. ./message.proto +//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) { var message ApplicationMetadataMessage diff --git a/protocol/v1/clock.go b/protocol/v1/clock.go index bf914951e..652b3dcd4 100644 --- a/protocol/v1/clock.go +++ b/protocol/v1/clock.go @@ -2,17 +2,17 @@ package protocol import "time" -const clockBumpInMs = int64(time.Minute / time.Millisecond) +const clockBumpInMs = uint64(time.Minute / time.Millisecond) // CalcMessageClock calculates a new clock value for Message. // It is used to properly sort messages and accommodate the fact // that time might be different on each device. -func CalcMessageClock(lastObservedValue int64, timeInMs TimestampInMs) int64 { +func CalcMessageClock(lastObservedValue uint64, timeInMs uint64) uint64 { clock := lastObservedValue - if clock < int64(timeInMs) { + if clock < timeInMs { // Added time should be larger than time skew tollerance for a message. // Here, we use 1 minute which is larger than accepted message time skew by Whisper. - clock = int64(timeInMs) + clockBumpInMs + clock = timeInMs + clockBumpInMs } else { clock++ } diff --git a/protocol/v1/decoder.go b/protocol/v1/decoder.go index 801011e21..efe29681a 100644 --- a/protocol/v1/decoder.go +++ b/protocol/v1/decoder.go @@ -1,7 +1,6 @@ package protocol import ( - "container/list" "errors" "fmt" "io" @@ -15,14 +14,12 @@ import ( func NewMessageDecoder(r io.Reader) *transit.Decoder { decoder := transit.NewDecoder(r) decoder.AddHandler(pairMessageTag, pairMessageHandler) - decoder.AddHandler(membershipUpdateTag, membershipUpdateMessageHandler) return decoder } const ( - messageTag = "c4" - pairMessageTag = "p2" - membershipUpdateTag = "g5" + messageTag = "c4" + pairMessageTag = "p2" ) func pairMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { @@ -60,83 +57,6 @@ func pairMessageHandler(d transit.Decoder, value interface{}) (interface{}, erro return pm, nil } -func membershipUpdateMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { - taggedValue, ok := value.(transit.TaggedValue) - if !ok { - return nil, errors.New("not a tagged value") - } - values, ok := taggedValue.Value.([]interface{}) - if !ok { - return nil, errors.New("tagged value does not contain values") - } - - m := MembershipUpdateMessage{} - for idx, v := range values { - var ok bool - - switch idx { - case 0: - m.ChatID, ok = v.(string) - case 1: - var updates *list.List - updates, ok = v.(*list.List) - if !ok { - break - } - for e := updates.Front(); e != nil; e = e.Next() { - var value map[interface{}]interface{} - value, ok = e.Value.(map[interface{}]interface{}) - if !ok { - break - } - - update := MembershipUpdate{} - - update.ChatID, ok = value[transit.Keyword("chat-id")].(string) - if !ok { - break - } - update.Signature, ok = value[transit.Keyword("signature")].(string) - if !ok { - break - } - - // parse events - var events []interface{} - events, ok = value[transit.Keyword("events")].([]interface{}) - if !ok { - break - } - for _, item := range events { - var event map[interface{}]interface{} - event, ok = item.(map[interface{}]interface{}) - if !ok { - break - } - - var updateEvent MembershipUpdateEvent - updateEvent, ok = parseEvent(event) - if !ok { - break - } - - update.Events = append(update.Events, updateEvent) - } - - m.Updates = append(m.Updates, update) - } - default: - // skip any other values - ok = true - } - - if !ok { - return nil, fmt.Errorf("invalid value for index: %d", idx) - } - } - return m, nil -} - func setToString(set *transit.Set) ([]string, bool) { result := make([]string, 0, len(set.Contents)) for _, item := range set.Contents { @@ -148,43 +68,3 @@ func setToString(set *transit.Set) ([]string, bool) { } return result, true } - -func parseEvent(event map[interface{}]interface{}) (result MembershipUpdateEvent, ok bool) { - // Type is required - result.Type, ok = event[transit.Keyword("type")].(string) - if !ok { - return - } - // ClockValue is required - result.ClockValue, ok = event[transit.Keyword("clock-value")].(int64) - if !ok { - return - } - // Name is optional - if val, exists := event[transit.Keyword("name")]; exists { - result.Name, ok = val.(string) - if !ok { - return - } - } - // Member is optional - if val, exists := event[transit.Keyword("member")]; exists { - result.Member, ok = val.(string) - if !ok { - return - } - } - // Members is optional - if val, exists := event[transit.Keyword("members")]; exists { - var members *transit.Set - members, ok = val.(*transit.Set) - if !ok { - return - } - result.Members, ok = setToString(members) - if !ok { - return - } - } - return -} diff --git a/protocol/v1/encoder.go b/protocol/v1/encoder.go index daf0755ef..b0678cea1 100644 --- a/protocol/v1/encoder.go +++ b/protocol/v1/encoder.go @@ -1,7 +1,6 @@ package protocol import ( - "container/list" "errors" "io" "reflect" @@ -10,8 +9,7 @@ import ( ) var ( - pairMessageType = reflect.TypeOf(PairMessage{}) - membershipUpdateType = reflect.TypeOf(MembershipUpdateMessage{}) + pairMessageType = reflect.TypeOf(PairMessage{}) defaultMessageValueEncoder = &messageValueEncoder{} ) @@ -22,7 +20,6 @@ var ( func NewMessageEncoder(w io.Writer) *transit.Encoder { encoder := transit.NewEncoder(w, false) encoder.AddHandler(pairMessageType, defaultMessageValueEncoder) - encoder.AddHandler(membershipUpdateType, defaultMessageValueEncoder) return encoder } @@ -45,47 +42,6 @@ func (messageValueEncoder) Encode(e transit.Encoder, value reflect.Value, asStri }, } return e.EncodeInterface(taggedValue, false) - case MembershipUpdateMessage: - updatesList := list.New() - for _, update := range message.Updates { - var events []interface{} - for _, event := range update.Events { - eventMap := map[interface{}]interface{}{ - transit.Keyword("type"): event.Type, - transit.Keyword("clock-value"): event.ClockValue, - } - if event.Name != "" { - eventMap[transit.Keyword("name")] = event.Name - } - if event.Member != "" { - eventMap[transit.Keyword("member")] = event.Member - } - if len(event.Members) > 0 { - members := make([]interface{}, len(event.Members)) - for idx, m := range event.Members { - members[idx] = m - } - eventMap[transit.Keyword("members")] = transit.NewSet(members) - } - events = append(events, eventMap) - } - - element := map[interface{}]interface{}{ - transit.Keyword("chat-id"): update.ChatID, - transit.Keyword("events"): events, - transit.Keyword("signature"): update.Signature, - } - updatesList.PushBack(element) - } - value := []interface{}{ - message.ChatID, - updatesList, - } - taggedValue := transit.TaggedValue{ - Tag: membershipUpdateTag, - Value: value, - } - return e.EncodeInterface(taggedValue, false) } return errors.New("unknown message type to encode") diff --git a/protocol/v1/membership_update_message.go b/protocol/v1/membership_update_message.go index 7d92a1be3..1a04065e8 100644 --- a/protocol/v1/membership_update_message.go +++ b/protocol/v1/membership_update_message.go @@ -3,145 +3,147 @@ package protocol import ( "bytes" "crypto/ecdsa" - "encoding/hex" - "encoding/json" "fmt" - "reflect" "sort" "strings" "time" + "github.com/golang/protobuf/proto" "github.com/google/uuid" "github.com/pkg/errors" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" -) - -const ( - MembershipUpdateChatCreated = "chat-created" - MembershipUpdateNameChanged = "name-changed" - MembershipUpdateMembersAdded = "members-added" - MembershipUpdateMemberJoined = "member-joined" - MembershipUpdateMemberRemoved = "member-removed" - MembershipUpdateAdminsAdded = "admins-added" - MembershipUpdateAdminRemoved = "admin-removed" + "github.com/status-im/status-go/protocol/protobuf" ) // MembershipUpdateMessage is a message used to propagate information // about group membership changes. // For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. type MembershipUpdateMessage struct { - ChatID string `json:"chatId"` // UUID concatenated with hex-encoded public key of the creator for the chat - Updates []MembershipUpdate `json:"updates"` + ChatID string `json:"chatId"` // UUID concatenated with hex-encoded public key of the creator for the chat + Events []MembershipUpdateEvent `json:"events"` + Message *protobuf.ChatMessage `json:"-"` } -// Verify makes sure that the received update message has a valid signature. -// It also extracts public key from the signature available as From field. -// It does not verify the updates and their events. This should be done -// separately using Group struct. -func (m *MembershipUpdateMessage) Verify() error { - for idx, update := range m.Updates { - if err := update.extractFrom(); err != nil { - return errors.Wrapf(err, "failed to extract an author of %d update", idx) - } - m.Updates[idx] = update +const signatureLength = 65 + +func MembershipUpdateEventFromProtobuf(chatID string, raw []byte) (*MembershipUpdateEvent, error) { + if len(raw) <= signatureLength { + return nil, errors.New("invalid payload length") } - return nil -} + decodedEvent := protobuf.MembershipUpdateEvent{} + signature := raw[:signatureLength] + encodedEvent := raw[signatureLength:] -// EncodeMembershipUpdateMessage encodes a MembershipUpdateMessage using Transit serialization. -func EncodeMembershipUpdateMessage(value MembershipUpdateMessage) ([]byte, error) { - var buf bytes.Buffer - encoder := NewMessageEncoder(&buf) - if err := encoder.Encode(value); err != nil { + signatureMaterial := append([]byte(chatID), encodedEvent...) + publicKey, err := crypto.ExtractSignature(signatureMaterial, signature) + if err != nil { + return nil, errors.Wrap(err, "failed to extract signature") + } + + from := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + + err = proto.Unmarshal(encodedEvent, &decodedEvent) + if err != nil { return nil, err } - return buf.Bytes(), nil + return &MembershipUpdateEvent{ + ClockValue: decodedEvent.Clock, + ChatID: chatID, + Members: decodedEvent.Members, + Name: decodedEvent.Name, + Type: decodedEvent.Type, + Signature: signature, + RawPayload: encodedEvent, + From: from, + }, nil } -type MembershipUpdate struct { - ChatID string `json:"chatId"` - Signature string `json:"signature"` // hex-encoded without 0x prefix - Events []MembershipUpdateEvent `json:"events"` - From string `json:"from"` // hex-encoded with 0x prefix +func (m *MembershipUpdateMessage) ToProtobuf() *protobuf.MembershipUpdateMessage { + var rawEvents [][]byte + for _, e := range m.Events { + var encodedEvent []byte + encodedEvent = append(encodedEvent, e.Signature...) + encodedEvent = append(encodedEvent, e.RawPayload...) + rawEvents = append(rawEvents, encodedEvent) + } + return &protobuf.MembershipUpdateMessage{ + ChatId: m.ChatID, + Events: rawEvents, + Message: m.Message, + } } -// Sign creates a signature from MembershipUpdateEvents -// and updates MembershipUpdate's signature. -// It follows the algorithm describe in the spec: -// https://github.com/status-im/specs/blob/master/status-group-chats-spec.md#signature. -func (u *MembershipUpdate) Sign(identity *ecdsa.PrivateKey) error { - signature, err := createMembershipUpdateSignature(u.ChatID, u.Events, identity) - if err != nil { - return err +func MembershipUpdateMessageFromProtobuf(raw *protobuf.MembershipUpdateMessage) (*MembershipUpdateMessage, error) { + var events []MembershipUpdateEvent + for _, e := range raw.Events { + verifiedEvent, err := MembershipUpdateEventFromProtobuf(raw.ChatId, e) + if err != nil { + return nil, err + } + events = append(events, *verifiedEvent) } - u.Signature = signature - return nil + return &MembershipUpdateMessage{ + ChatID: raw.ChatId, + Events: events, + Message: raw.Message, + }, nil } -func (u *MembershipUpdate) extractFrom() error { - content, err := stringifyMembershipUpdateEvents(u.ChatID, u.Events) - if err != nil { - return errors.Wrap(err, "failed to stringify events") - } - signatureBytes, err := hex.DecodeString(u.Signature) - if err != nil { - return errors.Wrap(err, "failed to decode signature") - } - publicKey, err := crypto.ExtractSignature(content, signatureBytes) - if err != nil { - return errors.Wrap(err, "failed to extract signature") - } - u.From = types.EncodeHex(crypto.FromECDSAPub(publicKey)) - return nil -} - -func (u *MembershipUpdate) Flat() []MembershipUpdateFlat { - result := make([]MembershipUpdateFlat, 0, len(u.Events)) - for _, event := range u.Events { - result = append(result, MembershipUpdateFlat{ - MembershipUpdateEvent: event, - ChatID: u.ChatID, - Signature: u.Signature, - From: u.From, - }) - } - return result +// EncodeMembershipUpdateMessage encodes a MembershipUpdateMessage using protobuf serialization. +func EncodeMembershipUpdateMessage(value MembershipUpdateMessage) ([]byte, error) { + return proto.Marshal(value.ToProtobuf()) } // MembershipUpdateEvent contains an event information. // Member and Members are hex-encoded values with 0x prefix. type MembershipUpdateEvent struct { - Type string `json:"type"` - ClockValue int64 `json:"clockValue"` - Member string `json:"member,omitempty"` // in "member-joined", "member-removed" and "admin-removed" events - Members []string `json:"members,omitempty"` // in "members-added" and "admins-added" events - Name string `json:"name,omitempty"` // name of the group chat + Type protobuf.MembershipUpdateEvent_EventType `json:"type"` + ClockValue uint64 `json:"clockValue"` + Members []string `json:"members,omitempty"` // in "members-added" and "admins-added" events + Name string `json:"name,omitempty"` // name of the group chat + From string + Signature []byte + ChatID string + RawPayload []byte } -func (u MembershipUpdateEvent) Equal(update MembershipUpdateEvent) bool { - return u.Type == update.Type && - u.ClockValue == update.ClockValue && - u.Member == update.Member && - stringSliceEquals(u.Members, update.Members) && - u.Name == update.Name +func (u *MembershipUpdateEvent) Equal(update MembershipUpdateEvent) bool { + return bytes.Compare(u.Signature, update.Signature) == 0 } -type MembershipUpdateFlat struct { - MembershipUpdateEvent - ChatID string `json:"chatId"` - Signature string `json:"signature"` - From string `json:"from"` +func (u *MembershipUpdateEvent) Sign(key *ecdsa.PrivateKey) error { + if len(u.ChatID) == 0 { + return errors.New("can't sign with empty chatID") + } + encodedEvent, err := proto.Marshal(u.ToProtobuf()) + if err != nil { + return err + } + u.RawPayload = encodedEvent + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, []byte(u.ChatID)...) + signatureMaterial = crypto.Keccak256(append(signatureMaterial, u.RawPayload...)) + signature, err := crypto.Sign(signatureMaterial, key) + + if err != nil { + return err + } + u.Signature = signature + u.From = types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)) + return nil } -func (u MembershipUpdateFlat) Equal(update MembershipUpdateFlat) bool { - return u.ChatID == update.ChatID && - u.Signature == update.Signature && - u.From == update.From && - u.MembershipUpdateEvent.Equal(update.MembershipUpdateEvent) +func (u *MembershipUpdateEvent) ToProtobuf() *protobuf.MembershipUpdateEvent { + return &protobuf.MembershipUpdateEvent{ + Clock: u.ClockValue, + Name: u.Name, + Members: u.Members, + Type: u.Type, + } } -func MergeFlatMembershipUpdates(dest []MembershipUpdateFlat, src []MembershipUpdateFlat) []MembershipUpdateFlat { +func MergeMembershipUpdateEvents(dest []MembershipUpdateEvent, src []MembershipUpdateEvent) []MembershipUpdateEvent { for _, update := range src { var exists bool for _, existing := range dest { @@ -157,166 +159,101 @@ func MergeFlatMembershipUpdates(dest []MembershipUpdateFlat, src []MembershipUpd return dest } -func NewChatCreatedEvent(name string, admin string, clock int64) MembershipUpdateEvent { +func NewChatCreatedEvent(name string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateChatCreated, - Name: name, - Member: admin, - ClockValue: clock, - } -} - -func NewNameChangedEvent(name string, clock int64) MembershipUpdateEvent { - return MembershipUpdateEvent{ - Type: MembershipUpdateNameChanged, + Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, Name: name, ClockValue: clock, } } -func NewMembersAddedEvent(members []string, clock int64) MembershipUpdateEvent { +func NewNameChangedEvent(name string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMembersAdded, + Type: protobuf.MembershipUpdateEvent_NAME_CHANGED, + Name: name, + ClockValue: clock, + } +} + +func NewMembersAddedEvent(members []string, clock uint64) MembershipUpdateEvent { + return MembershipUpdateEvent{ + Type: protobuf.MembershipUpdateEvent_MEMBERS_ADDED, Members: members, ClockValue: clock, } } -func NewMemberJoinedEvent(member string, clock int64) MembershipUpdateEvent { +func NewMemberJoinedEvent(clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMemberJoined, - Member: member, + Type: protobuf.MembershipUpdateEvent_MEMBER_JOINED, ClockValue: clock, } } -func NewAdminsAddedEvent(admins []string, clock int64) MembershipUpdateEvent { +func NewAdminsAddedEvent(admins []string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateAdminsAdded, + Type: protobuf.MembershipUpdateEvent_ADMINS_ADDED, Members: admins, ClockValue: clock, } } -func NewMemberRemovedEvent(member string, clock int64) MembershipUpdateEvent { +func NewMemberRemovedEvent(member string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMemberRemoved, - Member: member, + Type: protobuf.MembershipUpdateEvent_MEMBER_REMOVED, + Members: []string{member}, ClockValue: clock, } } -func NewAdminRemovedEvent(admin string, clock int64) MembershipUpdateEvent { +func NewAdminRemovedEvent(admin string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateAdminRemoved, - Member: admin, + Type: protobuf.MembershipUpdateEvent_ADMIN_REMOVED, + Members: []string{admin}, ClockValue: clock, } } -func stringifyMembershipUpdateEvents(chatID string, events []MembershipUpdateEvent) ([]byte, error) { - sort.Slice(events, func(i, j int) bool { - return events[i].ClockValue < events[j].ClockValue - }) - tuples := make([]interface{}, len(events)) - for idx, event := range events { - tuples[idx] = tupleMembershipUpdateEvent(event) - } - structureToSign := []interface{}{ - tuples, - chatID, - } - return json.Marshal(structureToSign) -} - -func createMembershipUpdateSignature(chatID string, events []MembershipUpdateEvent, identity *ecdsa.PrivateKey) (string, error) { - data, err := stringifyMembershipUpdateEvents(chatID, events) - if err != nil { - return "", err - } - return crypto.SignBytesAsHex(data, identity) -} - -var membershipUpdateEventFieldNamesCompat = map[string]string{ - "ClockValue": "clock-value", - "Name": "name", - "Type": "type", - "Member": "member", - "Members": "members", -} - -func tupleMembershipUpdateEvent(update MembershipUpdateEvent) [][]interface{} { - // Sort all slices first. - sort.Slice(update.Members, func(i, j int) bool { - return update.Members[i] < update.Members[j] - }) - v := reflect.ValueOf(update) - result := make([][]interface{}, 0, v.NumField()) - for i := 0; i < v.NumField(); i++ { - fieldName := v.Type().Field(i).Name - if name, exists := membershipUpdateEventFieldNamesCompat[fieldName]; exists { - fieldName = name - } - field := v.Field(i) - if !isZeroValue(field) { - result = append(result, []interface{}{fieldName, field.Interface()}) - } - } - // Sort the result lexicographically. - // We know that the first item of a tuple is a string - // because it's a field name. - sort.Slice(result, func(i, j int) bool { - return result[i][0].(string) < result[j][0].(string) - }) - return result -} - type Group struct { chatID string name string - updates []MembershipUpdateFlat + events []MembershipUpdateEvent admins *stringSet members *stringSet + joined *stringSet } func groupChatID(creator *ecdsa.PublicKey) string { return uuid.New().String() + "-" + types.EncodeHex(crypto.FromECDSAPub(creator)) } -func NewGroupWithMembershipUpdates(chatID string, updates []MembershipUpdate) (*Group, error) { - flatten := make([]MembershipUpdateFlat, 0, len(updates)) - for _, update := range updates { - flatten = append(flatten, update.Flat()...) - } - return newGroup(chatID, flatten) +func NewGroupWithEvents(chatID string, events []MembershipUpdateEvent) (*Group, error) { + return newGroup(chatID, events) } func NewGroupWithCreator(name string, creator *ecdsa.PrivateKey) (*Group, error) { chatID := groupChatID(&creator.PublicKey) - creatorHex := publicKeyToString(&creator.PublicKey) clock := TimestampInMsFromTime(time.Now()) - chatCreated := NewChatCreatedEvent(name, creatorHex, int64(clock)) - update := MembershipUpdate{ - ChatID: chatID, - From: creatorHex, - Events: []MembershipUpdateEvent{chatCreated}, - } - if err := update.Sign(creator); err != nil { + chatCreated := NewChatCreatedEvent(name, clock) + chatCreated.ChatID = chatID + err := chatCreated.Sign(creator) + if err != nil { return nil, err } - return newGroup(chatID, update.Flat()) + return newGroup(chatID, []MembershipUpdateEvent{chatCreated}) } -func NewGroup(chatID string, updates []MembershipUpdateFlat) (*Group, error) { - return newGroup(chatID, updates) +func NewGroup(chatID string, events []MembershipUpdateEvent) (*Group, error) { + return newGroup(chatID, events) } -func newGroup(chatID string, updates []MembershipUpdateFlat) (*Group, error) { +func newGroup(chatID string, events []MembershipUpdateEvent) (*Group, error) { g := Group{ chatID: chatID, - updates: updates, + events: events, admins: newStringSet(), members: newStringSet(), + joined: newStringSet(), } if err := g.init(); err != nil { return nil, err @@ -329,17 +266,17 @@ func (g *Group) init() error { var chatID string - for _, update := range g.updates { + for _, event := range g.events { if chatID == "" { - chatID = update.ChatID - } else if update.ChatID != chatID { + chatID = event.ChatID + } else if event.ChatID != chatID { return errors.New("updates contain different chat IDs") } - valid := g.validateEvent(update.From, update.MembershipUpdateEvent) + valid := g.validateEvent(event) if !valid { - return fmt.Errorf("invalid event %#+v from %s", update.MembershipUpdateEvent, update.From) + return fmt.Errorf("invalid event %#+v from %s", event, event.From) } - g.processEvent(update.From, update.MembershipUpdateEvent) + g.processEvent(event) } valid := g.validateChatID(g.chatID) @@ -357,8 +294,8 @@ func (g Group) ChatID() string { return g.chatID } -func (g Group) Updates() []MembershipUpdateFlat { - return g.updates +func (g Group) Events() []MembershipUpdateEvent { + return g.events } func (g Group) Name() string { @@ -374,18 +311,12 @@ func (g Group) Admins() []string { } func (g Group) Joined() []string { - var result []string - for _, update := range g.updates { - if update.Type == MembershipUpdateMemberJoined { - result = append(result, update.Member) - } - } - return result + return g.joined.List() } -func (g *Group) ProcessEvents(from *ecdsa.PublicKey, events []MembershipUpdateEvent) error { +func (g *Group) ProcessEvents(events []MembershipUpdateEvent) error { for _, event := range events { - err := g.ProcessEvent(from, event) + err := g.ProcessEvent(event) if err != nil { return err } @@ -393,38 +324,33 @@ func (g *Group) ProcessEvents(from *ecdsa.PublicKey, events []MembershipUpdateEv return nil } -func (g *Group) ProcessEvent(from *ecdsa.PublicKey, event MembershipUpdateEvent) error { - fromHex := types.EncodeHex(crypto.FromECDSAPub(from)) - if !g.validateEvent(fromHex, event) { - return fmt.Errorf("invalid event %#+v from %s", event, from) +func (g *Group) ProcessEvent(event MembershipUpdateEvent) error { + if !g.validateEvent(event) { + return fmt.Errorf("invalid event %#+v", event) } - update := MembershipUpdate{ - ChatID: g.chatID, - From: fromHex, - Events: []MembershipUpdateEvent{event}, - } - g.updates = append(g.updates, update.Flat()...) - g.processEvent(fromHex, event) + // Check if exists + g.events = append(g.events, event) + g.processEvent(event) return nil } -func (g Group) LastClockValue() int64 { - if len(g.updates) == 0 { +func (g Group) LastClockValue() uint64 { + if len(g.events) == 0 { return 0 } - return g.updates[len(g.updates)-1].ClockValue + return g.events[len(g.events)-1].ClockValue } -func (g Group) NextClockValue() int64 { +func (g Group) NextClockValue() uint64 { return g.LastClockValue() + 1 } func (g Group) creator() (string, error) { - if len(g.updates) == 0 { + if len(g.events) == 0 { return "", errors.New("no events in the group") } - first := g.updates[0] - if first.Type != MembershipUpdateChatCreated { + first := g.events[0] + if first.Type != protobuf.MembershipUpdateEvent_CHAT_CREATED { return "", fmt.Errorf("expected first event to be 'chat-created', got %s", first.Type) } return first.From, nil @@ -440,53 +366,63 @@ func (g Group) validateChatID(chatID string) bool { return strings.HasSuffix(chatID, creator) && chatID != creator } +func (g Group) IsMember(id string) bool { + return g.members.Has(id) +} + // validateEvent returns true if a given event is valid. -func (g Group) validateEvent(from string, event MembershipUpdateEvent) bool { +func (g Group) validateEvent(event MembershipUpdateEvent) bool { + if len(event.From) == 0 { + return false + } switch event.Type { - case MembershipUpdateChatCreated: + case protobuf.MembershipUpdateEvent_CHAT_CREATED: return g.admins.Empty() && g.members.Empty() - case MembershipUpdateNameChanged: - return g.admins.Has(from) && len(event.Name) > 0 - case MembershipUpdateMembersAdded: - return g.admins.Has(from) - case MembershipUpdateMemberJoined: - return g.members.Has(from) && from == event.Member - case MembershipUpdateMemberRemoved: + case protobuf.MembershipUpdateEvent_NAME_CHANGED: + return g.admins.Has(event.From) && len(event.Name) > 0 + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: + return g.admins.Has(event.From) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + return g.members.Has(event.From) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: // Member can remove themselves or admin can remove a member. - return from == event.Member || (g.admins.Has(from) && !g.admins.Has(event.Member)) - case MembershipUpdateAdminsAdded: - return g.admins.Has(from) && stringSliceSubset(event.Members, g.members.List()) - case MembershipUpdateAdminRemoved: - return g.admins.Has(from) && from == event.Member + return len(event.Members) == 1 && (event.From == event.Members[0] || (g.admins.Has(event.From) && !g.admins.Has(event.Members[0]))) + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: + return g.admins.Has(event.From) && stringSliceSubset(event.Members, g.members.List()) + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + return len(event.Members) == 1 && g.admins.Has(event.From) && event.From == event.Members[0] default: return false } } -func (g *Group) processEvent(from string, event MembershipUpdateEvent) { +func (g *Group) processEvent(event MembershipUpdateEvent) { switch event.Type { - case MembershipUpdateChatCreated: + case protobuf.MembershipUpdateEvent_CHAT_CREATED: g.name = event.Name - g.members.Add(event.Member) - g.admins.Add(event.Member) - case MembershipUpdateNameChanged: + g.members.Add(event.From) + g.joined.Add(event.From) + g.admins.Add(event.From) + case protobuf.MembershipUpdateEvent_NAME_CHANGED: g.name = event.Name - case MembershipUpdateAdminsAdded: + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: g.admins.Add(event.Members...) - case MembershipUpdateAdminRemoved: - g.admins.Remove(event.Member) - case MembershipUpdateMembersAdded: + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + g.admins.Remove(event.Members[0]) + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: g.members.Add(event.Members...) - case MembershipUpdateMemberRemoved: - g.members.Remove(event.Member) - case MembershipUpdateMemberJoined: - g.members.Add(event.Member) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: + g.admins.Remove(event.Members[0]) + g.joined.Remove(event.Members[0]) + g.members.Remove(event.Members[0]) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + g.joined.Add(event.From) } } func (g *Group) sortEvents() { - sort.Slice(g.updates, func(i, j int) bool { - return g.updates[i].ClockValue < g.updates[j].ClockValue + sort.Slice(g.events, func(i, j int) bool { + return g.events[i].ClockValue < g.events[j].ClockValue }) } diff --git a/protocol/v1/membership_update_message_test.go b/protocol/v1/membership_update_message_test.go index 7f6314171..8508e2f78 100644 --- a/protocol/v1/membership_update_message_test.go +++ b/protocol/v1/membership_update_message_test.go @@ -1,92 +1,63 @@ package protocol import ( - "encoding/hex" - "strings" "testing" - "unicode" + "github.com/golang/protobuf/proto" "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/protobuf" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var ( - testMembershipUpdateMessageBytes = []byte(`["~#g5",["072ea460-84d3-53c5-9979-1ca36fb5d1020x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1",["~#list",[["^ ","~:chat-id","072ea460-84d3-53c5-9979-1ca36fb5d1020x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1","~:events",[["^ ","~:type","chat-created","~:name","thathata","~:clock-value",156897373998501],["^ ","^4","members-added","^6",156897373998502,"~:members",["~#set",["0x04aebe2bb01a988abe7d978662f21de7760486119876c680e5a559e38e086a2df6dad41c4e4d9079c03db3bced6cb70fca76afc5650e50ea19b81572046a813534"]]]],"~:signature","7fca3d614cf55bc6cdf9c17fd1e65d1688673322bf1f004c58c78e0927edefea3d1053bf6a9d2e058ae88079f588105dccf2a2f9f330f6035cd47c715ee5950601"]]],null]]`) testMembershipUpdateMessageStruct = MembershipUpdateMessage{ - ChatID: "072ea460-84d3-53c5-9979-1ca36fb5d1020x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1", - Updates: []MembershipUpdate{ + ChatID: "chat-id", + Events: []MembershipUpdateEvent{ { - ChatID: "072ea460-84d3-53c5-9979-1ca36fb5d1020x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1", - Signature: "7fca3d614cf55bc6cdf9c17fd1e65d1688673322bf1f004c58c78e0927edefea3d1053bf6a9d2e058ae88079f588105dccf2a2f9f330f6035cd47c715ee5950601", - Events: []MembershipUpdateEvent{ - { - Type: MembershipUpdateChatCreated, - Name: "thathata", - ClockValue: 156897373998501, - }, - { - Type: MembershipUpdateMembersAdded, - Members: []string{"0x04aebe2bb01a988abe7d978662f21de7760486119876c680e5a559e38e086a2df6dad41c4e4d9079c03db3bced6cb70fca76afc5650e50ea19b81572046a813534"}, - ClockValue: 156897373998502, - }, - }, + Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, + Name: "thathata", + ChatID: "chat-id", + ClockValue: 156897373998501, + }, + { + Type: protobuf.MembershipUpdateEvent_MEMBERS_ADDED, + Members: []string{"0x04aebe2bb01a988abe7d978662f21de7760486119876c680e5a559e38e086a2df6dad41c4e4d9079c03db3bced6cb70fca76afc5650e50ea19b81572046a813534"}, + ChatID: "chat-id", + ClockValue: 156897373998502, }, }, } ) -func TestTupleMembershipUpdateEvent(t *testing.T) { - event1 := testMembershipUpdateMessageStruct.Updates[0].Events[0] - result1 := tupleMembershipUpdateEvent(event1) - require.EqualValues(t, [][]interface{}{ - {"clock-value", event1.ClockValue}, - {"name", "thathata"}, - {"type", "chat-created"}, - }, result1) - - event2 := testMembershipUpdateMessageStruct.Updates[0].Events[1] - result2 := tupleMembershipUpdateEvent(event2) - require.EqualValues(t, [][]interface{}{ - {"clock-value", event2.ClockValue}, - {"members", event2.Members}, - {"type", "members-added"}, - }, result2) -} - func TestSignMembershipUpdate(t *testing.T) { key, err := crypto.HexToECDSA("838fbdd1b670209a258b90af25653a018bc582c44c56e6290a973eebbeb15732") require.NoError(t, err) - update := testMembershipUpdateMessageStruct.Updates[0] - err = update.Sign(key) + event := &testMembershipUpdateMessageStruct.Events[0] + err = event.Sign(key) require.NoError(t, err) - expected, err := crypto.SignStringAsHex( - strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return -1 - } - return r - }, ` - [ - [ - [ - ["clock-value", 156897373998501], - ["name", "thathata"], - ["type", "chat-created"] - ], - [ - ["clock-value", 156897373998502], - ["members", ["0x04aebe2bb01a988abe7d978662f21de7760486119876c680e5a559e38e086a2df6dad41c4e4d9079c03db3bced6cb70fca76afc5650e50ea19b81572046a813534"]], - ["type", "members-added"] - ] - ], - "072ea460-84d3-53c5-9979-1ca36fb5d1020x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1" - ] - `), - key, - ) + + encodedEvent, err := proto.Marshal(event.ToProtobuf()) require.NoError(t, err) - require.Equal(t, expected, update.Signature) + + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, []byte(testMembershipUpdateMessageStruct.ChatID)...) + signatureMaterial = crypto.Keccak256(append(signatureMaterial, encodedEvent...)) + expected, err := crypto.Sign(signatureMaterial, key) + require.NoError(t, err) + require.Equal(t, encodedEvent, event.RawPayload) + require.Equal(t, expected, event.Signature) + + // Sign the other event + err = testMembershipUpdateMessageStruct.Events[1].Sign(key) + require.NoError(t, err) + + // Encode message + encodedMessage := testMembershipUpdateMessageStruct.ToProtobuf() + // Verify it + verifiedMessage, err := MembershipUpdateMessageFromProtobuf(encodedMessage) + require.NoError(t, err) + require.Equal(t, verifiedMessage, &testMembershipUpdateMessageStruct) } func TestGroupCreator(t *testing.T) { @@ -100,10 +71,11 @@ func TestGroupCreator(t *testing.T) { } func TestGroupProcessEvent(t *testing.T) { - createGroup := func(admins, members []string, name string) Group { + createGroup := func(admins, members, joined []string, name string) Group { return Group{ name: name, admins: newStringSetFromSlice(admins), + joined: newStringSetFromSlice(joined), members: newStringSetFromSlice(members), } } @@ -117,59 +89,60 @@ func TestGroupProcessEvent(t *testing.T) { }{ { Name: "chat-created event", - Group: createGroup(nil, nil, ""), - Result: createGroup([]string{"0xabc"}, []string{"0xabc"}, "some-name"), + Group: createGroup(nil, nil, nil, ""), + Result: createGroup([]string{"0xabc"}, []string{"0xabc"}, []string{"0xabc"}, "some-name"), From: "0xabc", - Event: NewChatCreatedEvent("some-name", "0xabc", 0), + Event: NewChatCreatedEvent("some-name", 0), }, { Name: "name-changed event", - Group: createGroup(nil, nil, ""), - Result: createGroup(nil, nil, "some-name"), + Group: createGroup(nil, nil, nil, ""), + Result: createGroup(nil, nil, nil, "some-name"), From: "0xabc", Event: NewNameChangedEvent("some-name", 0), }, { Name: "admins-added event", - Group: createGroup(nil, nil, ""), - Result: createGroup([]string{"0xabc", "0x123"}, nil, ""), + Group: createGroup(nil, nil, nil, ""), + Result: createGroup([]string{"0xabc", "0x123"}, nil, nil, ""), From: "0xabc", Event: NewAdminsAddedEvent([]string{"0xabc", "0x123"}, 0), }, { Name: "admin-removed event", - Group: createGroup([]string{"0xabc", "0xdef"}, nil, ""), - Result: createGroup([]string{"0xdef"}, nil, ""), + Group: createGroup([]string{"0xabc", "0xdef"}, nil, nil, ""), + Result: createGroup([]string{"0xdef"}, nil, nil, ""), From: "0xabc", Event: NewAdminRemovedEvent("0xabc", 0), }, { Name: "members-added event", - Group: createGroup(nil, nil, ""), - Result: createGroup(nil, []string{"0xabc", "0xdef"}, ""), + Group: createGroup(nil, nil, nil, ""), + Result: createGroup(nil, []string{"0xabc", "0xdef"}, nil, ""), From: "0xabc", Event: NewMembersAddedEvent([]string{"0xabc", "0xdef"}, 0), }, { Name: "member-removed event", - Group: createGroup(nil, []string{"0xabc", "0xdef"}, ""), - Result: createGroup(nil, []string{"0xdef"}, ""), + Group: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xdef", "0xabc"}, ""), + Result: createGroup(nil, []string{"0xdef"}, []string{"0xdef"}, ""), From: "0xabc", Event: NewMemberRemovedEvent("0xabc", 0), }, { Name: "member-joined event", - Group: createGroup(nil, []string{"0xabc"}, ""), - Result: createGroup(nil, []string{"0xabc", "0xdef"}, ""), - From: "0xabc", - Event: NewMemberJoinedEvent("0xdef", 0), + Group: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xabc"}, ""), + Result: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xabc", "0xdef"}, ""), + From: "0xdef", + Event: NewMemberJoinedEvent(0), }, } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { g := tc.Group - g.processEvent(tc.From, tc.Event) + tc.Event.From = tc.From + g.processEvent(tc.Event) require.EqualValues(t, tc.Result, g) }) } @@ -193,19 +166,22 @@ func TestGroupValidateEvent(t *testing.T) { { Name: "chat-created with empty admins and members", Group: createGroup(nil, nil), - Event: NewChatCreatedEvent("test", "0xabc", 0), + From: "0xabc", + Event: NewChatCreatedEvent("test", 0), Result: true, }, { Name: "chat-created with existing admins", Group: createGroup([]string{"0xabc"}, nil), - Event: NewChatCreatedEvent("test", "0xabc", 0), + From: "0xabc", + Event: NewChatCreatedEvent("test", 0), Result: false, }, { Name: "chat-created with existing members", Group: createGroup(nil, []string{"0xabc"}), - Event: NewChatCreatedEvent("test", "0xabc", 0), + From: "0xabc", + Event: NewChatCreatedEvent("test", 0), Result: false, }, { @@ -261,21 +237,21 @@ func TestGroupValidateEvent(t *testing.T) { Name: "member-joined must be in members", From: "0xabc", Group: createGroup(nil, []string{"0xabc"}), - Event: NewMemberJoinedEvent("0xabc", 0), + Event: NewMemberJoinedEvent(0), Result: true, }, { Name: "member-joined not valid because not in members", From: "0xabc", Group: createGroup(nil, nil), - Event: NewMemberJoinedEvent("0xabc", 0), + Event: NewMemberJoinedEvent(0), Result: false, }, { Name: "member-joined not valid because from differs from the event", From: "0xdef", Group: createGroup(nil, nil), - Event: NewMemberJoinedEvent("0xabc", 0), + Event: NewMemberJoinedEvent(0), Result: false, }, { @@ -324,101 +300,25 @@ func TestGroupValidateEvent(t *testing.T) { for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - result := tc.Group.validateEvent(tc.From, tc.Event) + tc.Event.From = tc.From + result := tc.Group.validateEvent(tc.Event) assert.Equal(t, tc.Result, result) }) } } -func TestMembershipUpdateMessageProcess(t *testing.T) { - key, err := crypto.GenerateKey() - require.NoError(t, err) - updates := []MembershipUpdate{ - { - ChatID: "some-chat", - Events: []MembershipUpdateEvent{ - NewChatCreatedEvent("some-name", "0xabc", 0), - }, - }, - } - err = updates[0].Sign(key) - require.NoError(t, err) - require.NotEmpty(t, updates[0].Signature) - - message := MembershipUpdateMessage{ - ChatID: "some-chat", - Updates: updates, - } - err = message.Verify() - require.NoError(t, err) - require.EqualValues(t, "0x"+hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey)), updates[0].From) -} - func TestMembershipUpdateEventEqual(t *testing.T) { u1 := MembershipUpdateEvent{ - Type: MembershipUpdateChatCreated, + Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, ClockValue: 1, - Member: "0xabc", Members: []string{"0xabc"}, Name: "abc", + Signature: []byte("signature"), } require.True(t, u1.Equal(u1)) // Verify equality breaking. u2 := u1 - u2.Members = append(u2.Members, "0xdef") - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.Type = MembershipUpdateMembersAdded - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.ClockValue = 2 - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.Member = "0xdef" - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.Name = "def" + u2.Signature = []byte("different-signature") require.False(t, u1.Equal(u2)) } - -func TestMembershipUpdateFlatEqual(t *testing.T) { - u1 := MembershipUpdateFlat{ - ChatID: "abc", - Signature: "abc", - From: "0xabc", - } - require.True(t, u1.Equal(u1)) - - // Verify equality breaking. - u2 := u1 - u2.ChatID = "def" - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.Signature = "def" - require.False(t, u1.Equal(u2)) - u2 = u1 - u2.From = "0xdef" - require.False(t, u1.Equal(u2)) -} - -func TestMergeFlatMembershipUpdates(t *testing.T) { - u1 := []MembershipUpdateFlat{ - { - ChatID: "abc", - Signature: "abc", - From: "0xabc", - }, - } - u2 := []MembershipUpdateFlat{ - { - ChatID: "abc", - Signature: "def", - From: "0xdef", - }, - } - result := MergeFlatMembershipUpdates(u1, u1) - require.EqualValues(t, u1, result) - result = MergeFlatMembershipUpdates(u1, u2) - require.EqualValues(t, append(u1, u2...), result) -} diff --git a/protocol/v1/message.go b/protocol/v1/message.go index afffcc2ab..df7b7c37e 100644 --- a/protocol/v1/message.go +++ b/protocol/v1/message.go @@ -17,34 +17,11 @@ var ( ErrInvalidDecodedValue = errors.New("invalid decoded value type") ) -// TimestampInMs is a timestamp in milliseconds. -type TimestampInMs int64 - -// Time returns a time.Time instance. -func (t TimestampInMs) Time() time.Time { - ts := int64(t) - seconds := ts / 1000 - return time.Unix(seconds, (ts%1000)*int64(time.Millisecond)) -} - // TimestampInMsFromTime returns a TimestampInMs from a time.Time instance. -func TimestampInMsFromTime(t time.Time) TimestampInMs { - return TimestampInMs(t.UnixNano() / int64(time.Millisecond)) +func TimestampInMsFromTime(t time.Time) uint64 { + return uint64(t.UnixNano() / int64(time.Millisecond)) } -// Flags define various boolean properties of a message. -type Flags uint64 - -func (f *Flags) Set(val Flags) { *f = *f | val } -func (f *Flags) Clear(val Flags) { *f = *f &^ val } -func (f *Flags) Toggle(val Flags) { *f = *f ^ val } -func (f Flags) Has(val Flags) bool { return f&val != 0 } - -// A list of Message flags. By default, a message is unread. -const ( - MessageRead Flags = 1 << iota -) - // MessageID calculates the messageID from author's compressed public key // and not encrypted but encoded payload. func MessageID(author *ecdsa.PublicKey, data []byte) types.HexBytes { @@ -53,7 +30,7 @@ func MessageID(author *ecdsa.PublicKey, data []byte) types.HexBytes { } // WrapMessageV1 wraps a payload into a protobuf message and signs it if an identity is provided -func WrapMessageV1(payload []byte, identity *ecdsa.PrivateKey) ([]byte, error) { +func WrapMessageV1(payload []byte, messageType protobuf.ApplicationMetadataMessage_Type, identity *ecdsa.PrivateKey) ([]byte, error) { var signature []byte if identity != nil { var err error @@ -65,6 +42,7 @@ func WrapMessageV1(payload []byte, identity *ecdsa.PrivateKey) ([]byte, error) { message := &protobuf.ApplicationMetadataMessage{ Signature: signature, + Type: messageType, Payload: payload, } return proto.Marshal(message) diff --git a/protocol/v1/message_test.go b/protocol/v1/message_test.go index 3aa37c1a3..7b6b555c2 100644 --- a/protocol/v1/message_test.go +++ b/protocol/v1/message_test.go @@ -2,7 +2,6 @@ package protocol import ( "testing" - "time" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" @@ -19,10 +18,3 @@ func TestMessageID(t *testing.T) { expectedID := types.HexBytes(crypto.Keccak256(append(keyBytes, data...))) require.Equal(t, expectedID, MessageID(&key.PublicKey, data)) } - -func TestTimestampInMs(t *testing.T) { - ts := TimestampInMs(1555274502548) // random timestamp in milliseconds - tt := ts.Time() - require.Equal(t, tt.UnixNano(), 1555274502548*int64(time.Millisecond)) - require.Equal(t, ts, TimestampInMsFromTime(tt)) -} diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index 7d9cb9321..656509c1b 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -17,18 +17,12 @@ import ( type StatusMessageT int -const ( - MessageT StatusMessageT = iota + 1 - MembershipUpdateMessageT - PairMessageT -) - // StatusMessage is any Status Protocol message. type StatusMessage struct { // TransportMessage is the parsed message received from the transport layer, i.e the input TransportMessage *types.Message `json:"transportMessage"` - // MessageType is the type of application message contained - MessageType StatusMessageT `json:"-"` + // Type is the type of application message contained + Type protobuf.ApplicationMetadataMessage_Type `json:"-"` // ParsedMessage is the parsed message by the application layer, i.e the output ParsedMessage interface{} `json:"-"` @@ -157,23 +151,37 @@ func (m *StatusMessage) HandleApplicationMetadata() error { // Calculate ID using the wrapped record m.ID = MessageID(recoveredKey, m.DecryptedPayload) m.DecryptedPayload = message.Payload + m.Type = message.Type return nil } func (m *StatusMessage) HandleApplication() error { - // Try protobuf first - var message protobuf.ChatMessage + switch m.Type { + case protobuf.ApplicationMetadataMessage_CHAT_MESSAGE: + var message protobuf.ChatMessage - err := proto.Unmarshal(m.DecryptedPayload, &message) - if err != nil { - m.ParsedMessage = nil - log.Printf("[message::DecodeMessage] could not decode protobuf message: %#x, err: %v", m.Hash, err.Error()) - } else { - m.MessageType = MessageT - m.ParsedMessage = message + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode ChatMessage: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message - return nil + return nil + } + case protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE: + var message protobuf.MembershipUpdateMessage + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode MembershipUpdateMessage: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } } + return nil } diff --git a/services/shhext/api.go b/services/shhext/api.go index a0bae7cf5..67e3033dc 100644 --- a/services/shhext/api.go +++ b/services/shhext/api.go @@ -21,7 +21,6 @@ import ( "github.com/status-im/status-go/whisper/v6" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" enstypes "github.com/status-im/status-go/eth-node/types/ens" "github.com/status-im/status-go/protocol" @@ -448,17 +447,46 @@ func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg SendPublicMessa // It's important to call PublicAPI.afterSend() so that the client receives a signal // with confirmation that the message left the device. func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg SendDirectMessageRPC) (types.HexBytes, error) { - publicKey, err := crypto.UnmarshalPubkey(msg.PubKey) - if err != nil { - return nil, err - } chat := protocol.Chat{ - PublicKey: publicKey, + ChatType: protocol.ChatTypeOneToOne, + ID: types.EncodeHex(msg.PubKey), } return api.service.messenger.SendRaw(ctx, chat, msg.Payload) } +func (api *PublicAPI) Join(chat protocol.Chat) error { + return api.service.messenger.Join(chat) +} + +func (api *PublicAPI) Leave(chat protocol.Chat) error { + return api.service.messenger.Leave(chat) +} + +func (api *PublicAPI) LeaveGroupChat(ctx Context, chatID string) (*protocol.MessengerResponse, error) { + return api.service.messenger.LeaveGroupChat(ctx, chatID) +} + +func (api *PublicAPI) CreateGroupChatWithMembers(ctx Context, name string, members []string) (*protocol.MessengerResponse, error) { + return api.service.messenger.CreateGroupChatWithMembers(ctx, name, members) +} + +func (api *PublicAPI) AddMembersToGroupChat(ctx Context, chatID string, members []string) (*protocol.MessengerResponse, error) { + return api.service.messenger.AddMembersToGroupChat(ctx, chatID, members) +} + +func (api *PublicAPI) RemoveMemberFromGroupChat(ctx Context, chatID string, member string) (*protocol.MessengerResponse, error) { + return api.service.messenger.RemoveMemberFromGroupChat(ctx, chatID, member) +} + +func (api *PublicAPI) AddAdminsToGroupChat(ctx Context, chatID string, members []string) (*protocol.MessengerResponse, error) { + return api.service.messenger.AddAdminsToGroupChat(ctx, chatID, members) +} + +func (api *PublicAPI) ConfirmJoiningGroup(ctx context.Context, chatID string) (*protocol.MessengerResponse, error) { + return api.service.messenger.ConfirmJoiningGroup(ctx, chatID) +} + func (api *PublicAPI) requestMessagesUsingPayload(request db.HistoryRequest, peer, symkeyID string, payload []byte, force bool, timeout time.Duration, topics []types.TopicType) (hash types.Hash, err error) { shh := api.service.w now := api.service.w.GetCurrentTime() @@ -571,12 +599,12 @@ func (api *PublicAPI) LoadFilters(parent context.Context, chats []*statustransp. return api.service.messenger.LoadFilters(chats) } -func (api *PublicAPI) SaveChat(parent context.Context, chat protocol.Chat) error { +func (api *PublicAPI) SaveChat(parent context.Context, chat *protocol.Chat) error { api.log.Info("saving chat", "chat", chat) return api.service.messenger.SaveChat(chat) } -func (api *PublicAPI) Chats(parent context.Context) ([]*protocol.Chat, error) { +func (api *PublicAPI) Chats(parent context.Context) []*protocol.Chat { return api.service.messenger.Chats() } @@ -584,16 +612,16 @@ func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error { return api.service.messenger.DeleteChat(chatID) } -func (api *PublicAPI) SaveContact(parent context.Context, contact protocol.Contact) error { +func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error { return api.service.messenger.SaveContact(contact) } -func (api *PublicAPI) BlockContact(parent context.Context, contact protocol.Contact) ([]*protocol.Chat, error) { +func (api *PublicAPI) BlockContact(parent context.Context, contact *protocol.Contact) ([]*protocol.Chat, error) { api.log.Info("blocking contact", "contact", contact.ID) return api.service.messenger.BlockContact(contact) } -func (api *PublicAPI) Contacts(parent context.Context) ([]*protocol.Contact, error) { +func (api *PublicAPI) Contacts(parent context.Context) []*protocol.Contact { return api.service.messenger.Contacts() } @@ -643,10 +671,6 @@ func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*Applicati }, nil } -func (api *PublicAPI) AddSystemMessages(messages []*protocol.Message) ([]*protocol.Message, error) { - return api.service.messenger.AddSystemMessages(messages) -} - func (api *PublicAPI) DeleteMessage(id string) error { return api.service.messenger.DeleteMessage(id) } diff --git a/services/shhext/service.go b/services/shhext/service.go index 9562fad03..5eae83a58 100644 --- a/services/shhext/service.go +++ b/services/shhext/service.go @@ -151,7 +151,7 @@ func (s *Service) InitProtocol(db *sql.DB) error { // nolint: gocyclo s.cancelMessenger = make(chan struct{}) go s.retrieveMessagesLoop(time.Second, s.cancelMessenger) - return nil + return s.messenger.Init() } func (s *Service) retrieveMessagesLoop(tick time.Duration, cancel <-chan struct{}) { diff --git a/vendor/github.com/elastic/gosigar/CHANGELOG.md b/vendor/github.com/elastic/gosigar/CHANGELOG.md index ae848ffb1..9377542ab 100644 --- a/vendor/github.com/elastic/gosigar/CHANGELOG.md +++ b/vendor/github.com/elastic/gosigar/CHANGELOG.md @@ -12,17 +12,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Deprecated -## [0.10.5] - -### Fixed - -- Fixed uptime calculation under Windows. #126 -- Fixed compilation issue for darwin/386. #128 - -### Changed - -- Load DLLs only from Windows system directory. #132 - ## [0.10.4] ### Fixed diff --git a/vendor/github.com/elastic/gosigar/README.md b/vendor/github.com/elastic/gosigar/README.md index ca1854bf2..ecdfc1c3c 100644 --- a/vendor/github.com/elastic/gosigar/README.md +++ b/vendor/github.com/elastic/gosigar/README.md @@ -37,7 +37,6 @@ The features vary by operating system. | ProcMem | X | X | X | | X | | ProcState | X | X | X | | X | | ProcTime | X | X | X | | X | -| Rusage | X | | X | | | | Swap | X | X | | X | X | | Uptime | X | X | | X | X | diff --git a/vendor/github.com/elastic/gosigar/sigar_darwin.go b/vendor/github.com/elastic/gosigar/sigar_darwin.go index 4a8309521..a90b998c2 100644 --- a/vendor/github.com/elastic/gosigar/sigar_darwin.go +++ b/vendor/github.com/elastic/gosigar/sigar_darwin.go @@ -40,6 +40,18 @@ func (self *LoadAverage) Get() error { return nil } +func (self *Uptime) Get() error { + tv := syscall.Timeval32{} + + if err := sysctlbyname("kern.boottime", &tv); err != nil { + return err + } + + self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds() + + return nil +} + func (self *Mem) Get() error { var vmstat C.vm_statistics_data_t diff --git a/vendor/github.com/elastic/gosigar/sigar_darwin_386.go b/vendor/github.com/elastic/gosigar/sigar_darwin_386.go deleted file mode 100644 index 92c7ff040..000000000 --- a/vendor/github.com/elastic/gosigar/sigar_darwin_386.go +++ /dev/null @@ -1,18 +0,0 @@ -package gosigar - -import ( - "syscall" - "time" -) - -func (self *Uptime) Get() error { - tv := syscall.Timeval{} - - if err := sysctlbyname("kern.boottime", &tv); err != nil { - return err - } - - self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds() - - return nil -} diff --git a/vendor/github.com/elastic/gosigar/sigar_darwin_amd64.go b/vendor/github.com/elastic/gosigar/sigar_darwin_amd64.go deleted file mode 100644 index 29e5b604a..000000000 --- a/vendor/github.com/elastic/gosigar/sigar_darwin_amd64.go +++ /dev/null @@ -1,18 +0,0 @@ -package gosigar - -import ( - "syscall" - "time" -) - -func (self *Uptime) Get() error { - tv := syscall.Timeval32{} - - if err := sysctlbyname("kern.boottime", &tv); err != nil { - return err - } - - self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds() - - return nil -} diff --git a/vendor/github.com/elastic/gosigar/sigar_interface.go b/vendor/github.com/elastic/gosigar/sigar_interface.go index 57501b969..df79ae08d 100644 --- a/vendor/github.com/elastic/gosigar/sigar_interface.go +++ b/vendor/github.com/elastic/gosigar/sigar_interface.go @@ -4,7 +4,6 @@ import ( "time" ) -// ErrNotImplemented is returned when a particular statistic isn't implemented on the host OS. type ErrNotImplemented struct { OS string } @@ -13,7 +12,6 @@ func (e ErrNotImplemented) Error() string { return "not implemented on " + e.OS } -// IsNotImplemented returns true if the error is ErrNotImplemented func IsNotImplemented(err error) bool { switch err.(type) { case ErrNotImplemented, *ErrNotImplemented: @@ -23,7 +21,6 @@ func IsNotImplemented(err error) bool { } } -// Sigar is an interface for gathering system host stats type Sigar interface { CollectCpuStats(collectionInterval time.Duration) (<-chan Cpu, chan<- struct{}) GetLoadAverage() (LoadAverage, error) @@ -35,7 +32,6 @@ type Sigar interface { GetRusage(who int) (Rusage, error) } -// Cpu contains CPU time stats type Cpu struct { User uint64 Nice uint64 @@ -47,13 +43,11 @@ type Cpu struct { Stolen uint64 } -// Total returns total CPU time func (cpu *Cpu) Total() uint64 { return cpu.User + cpu.Nice + cpu.Sys + cpu.Idle + cpu.Wait + cpu.Irq + cpu.SoftIrq + cpu.Stolen } -// Delta returns the difference between two Cpu stat objects func (cpu Cpu) Delta(other Cpu) Cpu { return Cpu{ User: cpu.User - other.User, @@ -67,17 +61,14 @@ func (cpu Cpu) Delta(other Cpu) Cpu { } } -// LoadAverage reports standard load averages type LoadAverage struct { One, Five, Fifteen float64 } -// Uptime reports system uptime type Uptime struct { Length float64 } -// Mem contains host memory stats type Mem struct { Total uint64 Used uint64 @@ -86,14 +77,12 @@ type Mem struct { ActualUsed uint64 } -// Swap contains stats on swap space type Swap struct { Total uint64 Used uint64 Free uint64 } -// HugeTLBPages contains HugePages stats type HugeTLBPages struct { Total uint64 Free uint64 @@ -103,19 +92,16 @@ type HugeTLBPages struct { TotalAllocatedSize uint64 } -// CpuList contains a list of CPUs on the host system type CpuList struct { List []Cpu } -// FDUsage contains stats on filesystem usage type FDUsage struct { Open uint64 Unused uint64 Max uint64 } -// FileSystem contains basic information about a given mounted filesystem type FileSystem struct { DirName string DevName string @@ -125,12 +111,10 @@ type FileSystem struct { Flags uint32 } -// FileSystemList gets a list of mounted filesystems type FileSystemList struct { List []FileSystem } -// FileSystemUsage contains basic stats for the specified filesystem type FileSystemUsage struct { Total uint64 Used uint64 @@ -140,30 +124,21 @@ type FileSystemUsage struct { FreeFiles uint64 } -// ProcList contains a list of processes found on the host system type ProcList struct { List []int } -// RunState is a byte-long code used to specify the current runtime state of a process type RunState byte const ( - // RunStateSleep corresponds to a sleep state - RunStateSleep = 'S' - // RunStateRun corresponds to a running state - RunStateRun = 'R' - // RunStateStop corresponds to a stopped state - RunStateStop = 'T' - // RunStateZombie marks a zombie process - RunStateZombie = 'Z' - // RunStateIdle corresponds to an idle state - RunStateIdle = 'D' - // RunStateUnknown corresponds to a process in an unknown state + RunStateSleep = 'S' + RunStateRun = 'R' + RunStateStop = 'T' + RunStateZombie = 'Z' + RunStateIdle = 'D' RunStateUnknown = '?' ) -// ProcState contains basic metadata and process ownership info for the specified process type ProcState struct { Name string Username string @@ -176,7 +151,6 @@ type ProcState struct { Processor int } -// ProcMem contains memory statistics for a specified process type ProcMem struct { Size uint64 Resident uint64 @@ -186,7 +160,6 @@ type ProcMem struct { PageFaults uint64 } -// ProcTime contains run time statistics for a specified process type ProcTime struct { StartTime uint64 User uint64 @@ -194,31 +167,26 @@ type ProcTime struct { Total uint64 } -// ProcArgs contains a list of args for a specified process type ProcArgs struct { List []string } -// ProcEnv contains a map of environment variables for specified process type ProcEnv struct { Vars map[string]string } -// ProcExe contains basic data about a specified process type ProcExe struct { Name string Cwd string Root string } -// ProcFDUsage contains data on file limits and usage type ProcFDUsage struct { Open uint64 SoftLimit uint64 HardLimit uint64 } -// Rusage contains data on resource usage for a specified process type Rusage struct { Utime time.Duration Stime time.Duration diff --git a/vendor/github.com/elastic/gosigar/sigar_windows.go b/vendor/github.com/elastic/gosigar/sigar_windows.go index d1204b80e..fc868daf3 100644 --- a/vendor/github.com/elastic/gosigar/sigar_windows.go +++ b/vendor/github.com/elastic/gosigar/sigar_windows.go @@ -8,6 +8,7 @@ import ( "path/filepath" "runtime" "strings" + "sync" "syscall" "time" @@ -23,6 +24,11 @@ var ( // 2003 and XP where PROCESS_QUERY_LIMITED_INFORMATION is unknown. For all newer // OS versions it is set to PROCESS_QUERY_LIMITED_INFORMATION. processQueryLimitedInfoAccess = windows.PROCESS_QUERY_LIMITED_INFORMATION + + // bootTime is the time when the OS was last booted. This value may be nil + // on operating systems that do not support the WMI query used to obtain it. + bootTime *time.Time + bootTimeLock sync.Mutex ) func init() { @@ -57,11 +63,19 @@ func (self *Uptime) Get() error { if !version.IsWindowsVistaOrGreater() { return ErrNotImplemented{runtime.GOOS} } - uptimeMs, err := windows.GetTickCount64() - if err != nil { - return errors.Wrap(err, "failed to get boot time using GetTickCount64 api") + + bootTimeLock.Lock() + defer bootTimeLock.Unlock() + if bootTime == nil { + uptime, err := windows.GetTickCount64() + if err != nil { + return errors.Wrap(err, "failed to get boot time using win32 api") + } + var boot = time.Unix(int64(uptime), 0) + bootTime = &boot } - self.Length = float64(time.Duration(uptimeMs)*time.Millisecond) / float64(time.Second) + + self.Length = time.Since(*bootTime).Seconds() return nil } diff --git a/vendor/github.com/elastic/gosigar/sys/windows/doc.go b/vendor/github.com/elastic/gosigar/sys/windows/doc.go index 9dca12504..dda57aa83 100644 --- a/vendor/github.com/elastic/gosigar/sys/windows/doc.go +++ b/vendor/github.com/elastic/gosigar/sys/windows/doc.go @@ -1,8 +1,2 @@ // Package windows contains various Windows system call. package windows - -// Use "go generate -v -x ." to generate the source. - -// Add -trace to enable debug prints around syscalls. -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=true -output zsyscall_windows.go syscall_windows.go -//go:generate go run fix_generated.go -input zsyscall_windows.go diff --git a/vendor/github.com/elastic/gosigar/sys/windows/syscall_windows.go b/vendor/github.com/elastic/gosigar/sys/windows/syscall_windows.go index 371eb256b..0c11fda31 100644 --- a/vendor/github.com/elastic/gosigar/sys/windows/syscall_windows.go +++ b/vendor/github.com/elastic/gosigar/sys/windows/syscall_windows.go @@ -580,6 +580,11 @@ func GetTickCount64() (uptime uint64, err error) { return uptime, nil } +// Use "GOOS=windows go generate -v -x ." to generate the source. + +// Add -trace to enable debug prints around syscalls. +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=false -output zsyscall_windows.go syscall_windows.go + // Windows API calls //sys _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) = kernel32.GlobalMemoryStatusEx //sys _GetLogicalDriveStringsW(bufferLength uint32, buffer *uint16) (length uint32, err error) = kernel32.GetLogicalDriveStringsW diff --git a/vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go b/vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go index 75f19c0ea..cd5d9ca32 100644 --- a/vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go +++ b/vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go @@ -5,8 +5,6 @@ package windows import ( "syscall" "unsafe" - - "golang.org/x/sys/windows" ) var _ unsafe.Pointer @@ -37,10 +35,10 @@ func errnoErr(e syscall.Errno) error { } var ( - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - modpsapi = windows.NewLazySystemDLL("psapi.dll") - modntdll = windows.NewLazySystemDLL("ntdll.dll") - modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + modpsapi = syscall.NewLazyDLL("psapi.dll") + modntdll = syscall.NewLazyDLL("ntdll.dll") + modadvapi32 = syscall.NewLazyDLL("advapi32.dll") procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") diff --git a/vendor/github.com/gballet/go-libpcsclite/error.go b/vendor/github.com/gballet/go-libpcsclite/error.go index 87cfee3d0..4710de5e6 100644 --- a/vendor/github.com/gballet/go-libpcsclite/error.go +++ b/vendor/github.com/gballet/go-libpcsclite/error.go @@ -36,66 +36,66 @@ type ErrorCode uint32 const ( SCardSuccess ErrorCode = 0x00000000 /* No error was encountered. */ - ErrSCardInternal ErrorCode = 0x80100001 /* An internal consistency check failed. */ - ErrSCardCancelled ErrorCode = 0x80100002 /* The action was cancelled by an SCardCancel request. */ - ErrSCardInvalidHandle ErrorCode = 0x80100003 /* The supplied handle was invalid. */ - ErrSCardInvalidParameter ErrorCode = 0x80100004 /* One or more of the supplied parameters could not be properly interpreted. */ - ErrSCardInvalidTarget ErrorCode = 0x80100005 /* Registry startup information is missing or invalid. */ - ErrSCardNoMemory ErrorCode = 0x80100006 /* Not enough memory available to complete this command. */ - ErrSCardWaitedTooLong ErrorCode = 0x80100007 /* An internal consistency timer has expired. */ - ErrSCardInsufficientBuffer ErrorCode = 0x80100008 /* The data buffer to receive returned data is too small for the returned data. */ - ErrScardUnknownReader ErrorCode = 0x80100009 /* The specified reader name is not recognized. */ - ErrSCardTimeout ErrorCode = 0x8010000A /* The user-specified timeout value has expired. */ - ErrSCardSharingViolation ErrorCode = 0x8010000B /* The smart card cannot be accessed because of other connections outstanding. */ - ErrSCardNoSmartCard ErrorCode = 0x8010000C /* The operation requires a Smart Card, but no Smart Card is currently in the device. */ - ErrSCardUnknownCard ErrorCode = 0x8010000D /* The specified smart card name is not recognized. */ - ErrSCardCannotDispose ErrorCode = 0x8010000E /* The system could not dispose of the media in the requested manner. */ - ErrSCardProtoMismatch ErrorCode = 0x8010000F /* The requested protocols are incompatible with the protocol currently in use with the smart card. */ - ErrSCardNotReady ErrorCode = 0x80100010 /* The reader or smart card is not ready to accept commands. */ - ErrSCardInvalidValue ErrorCode = 0x80100011 /* One or more of the supplied parameters values could not be properly interpreted. */ - ErrSCardSystemCancelled ErrorCode = 0x80100012 /* The action was cancelled by the system, presumably to log off or shut down. */ - ErrSCardCommError ErrorCode = 0x80100013 /* An internal communications error has been detected. */ - ErrScardUnknownError ErrorCode = 0x80100014 /* An internal error has been detected, but the source is unknown. */ - ErrSCardInvalidATR ErrorCode = 0x80100015 /* An ATR obtained from the registry is not a valid ATR string. */ - ErrSCardNotTransacted ErrorCode = 0x80100016 /* An attempt was made to end a non-existent transaction. */ - ErrSCardReaderUnavailable ErrorCode = 0x80100017 /* The specified reader is not currently available for use. */ - ErrSCardShutdown ErrorCode = 0x80100018 /* The operation has been aborted to allow the server application to exit. */ - ErrSCardPCITooSmall ErrorCode = 0x80100019 /* The PCI Receive buffer was too small. */ - ErrSCardReaderUnsupported ErrorCode = 0x8010001A /* The reader driver does not meet minimal requirements for support. */ - ErrSCardDuplicateReader ErrorCode = 0x8010001B /* The reader driver did not produce a unique reader name. */ - ErrSCardCardUnsupported ErrorCode = 0x8010001C /* The smart card does not meet minimal requirements for support. */ - ErrScardNoService ErrorCode = 0x8010001D /* The Smart card resource manager is not running. */ - ErrSCardServiceStopped ErrorCode = 0x8010001E /* The Smart card resource manager has shut down. */ - ErrSCardUnexpected ErrorCode = 0x8010001F /* An unexpected card error has occurred. */ - ErrSCardUnsupportedFeature ErrorCode = 0x8010001F /* This smart card does not support the requested feature. */ - ErrSCardICCInstallation ErrorCode = 0x80100020 /* No primary provider can be found for the smart card. */ - ErrSCardICCCreateOrder ErrorCode = 0x80100021 /* The requested order of object creation is not supported. */ - ErrSCardDirNotFound ErrorCode = 0x80100023 /* The identified directory does not exist in the smart card. */ - ErrSCardFileNotFound ErrorCode = 0x80100024 /* The identified file does not exist in the smart card. */ - ErrSCardNoDir ErrorCode = 0x80100025 /* The supplied path does not represent a smart card directory. */ - ErrSCardNoFile ErrorCode = 0x80100026 /* The supplied path does not represent a smart card file. */ - ErrScardNoAccess ErrorCode = 0x80100027 /* Access is denied to this file. */ - ErrSCardWriteTooMany ErrorCode = 0x80100028 /* The smart card does not have enough memory to store the information. */ - ErrSCardBadSeek ErrorCode = 0x80100029 /* There was an error trying to set the smart card file object pointer. */ - ErrSCardInvalidCHV ErrorCode = 0x8010002A /* The supplied PIN is incorrect. */ - ErrSCardUnknownResMNG ErrorCode = 0x8010002B /* An unrecognized error code was returned from a layered component. */ - ErrSCardNoSuchCertificate ErrorCode = 0x8010002C /* The requested certificate does not exist. */ - ErrSCardCertificateUnavailable ErrorCode = 0x8010002D /* The requested certificate could not be obtained. */ - ErrSCardNoReadersAvailable ErrorCode = 0x8010002E /* Cannot find a smart card reader. */ - ErrSCardCommDataLost ErrorCode = 0x8010002F /* A communications error with the smart card has been detected. Retry the operation. */ - ErrScardNoKeyContainer ErrorCode = 0x80100030 /* The requested key container does not exist on the smart card. */ - ErrSCardServerTooBusy ErrorCode = 0x80100031 /* The Smart Card Resource Manager is too busy to complete this operation. */ - ErrSCardUnsupportedCard ErrorCode = 0x80100065 /* The reader cannot communicate with the card, due to ATR string configuration conflicts. */ - ErrSCardUnresponsiveCard ErrorCode = 0x80100066 /* The smart card is not responding to a reset. */ - ErrSCardUnpoweredCard ErrorCode = 0x80100067 /* Power has been removed from the smart card, so that further communication is not possible. */ - ErrSCardResetCard ErrorCode = 0x80100068 /* The smart card has been reset, so any shared state information is invalid. */ - ErrSCardRemovedCard ErrorCode = 0x80100069 /* The smart card has been removed, so further communication is not possible. */ - ErrSCardSecurityViolation ErrorCode = 0x8010006A /* Access was denied because of a security violation. */ - ErrSCardWrongCHV ErrorCode = 0x8010006B /* The card cannot be accessed because the wrong PIN was presented. */ - ErrSCardCHVBlocked ErrorCode = 0x8010006C /* The card cannot be accessed because the maximum number of PIN entry attempts has been reached. */ - ErrSCardEOF ErrorCode = 0x8010006D /* The end of the smart card file has been reached. */ - ErrSCardCancelledByUser ErrorCode = 0x8010006E /* The user pressed "Cancel" on a Smart Card Selection Dialog. */ - ErrSCardCardNotAuthenticated ErrorCode = 0x8010006F /* No PIN was presented to the smart card. */ + ErrSCardInternal = 0x80100001 /* An internal consistency check failed. */ + ErrSCardCancelled = 0x80100002 /* The action was cancelled by an SCardCancel request. */ + ErrSCardInvalidHandle = 0x80100003 /* The supplied handle was invalid. */ + ErrSCardInvalidParameter = 0x80100004 /* One or more of the supplied parameters could not be properly interpreted. */ + ErrSCardInvalidTarget = 0x80100005 /* Registry startup information is missing or invalid. */ + ErrSCardNoMemory = 0x80100006 /* Not enough memory available to complete this command. */ + ErrSCardWaitedTooLong = 0x80100007 /* An internal consistency timer has expired. */ + ErrSCardInsufficientBuffer = 0x80100008 /* The data buffer to receive returned data is too small for the returned data. */ + ErrScardUnknownReader = 0x80100009 /* The specified reader name is not recognized. */ + ErrSCardTimeout = 0x8010000A /* The user-specified timeout value has expired. */ + ErrSCardSharingViolation = 0x8010000B /* The smart card cannot be accessed because of other connections outstanding. */ + ErrSCardNoSmartCard = 0x8010000C /* The operation requires a Smart Card, but no Smart Card is currently in the device. */ + ErrSCardUnknownCard = 0x8010000D /* The specified smart card name is not recognized. */ + ErrSCardCannotDispose = 0x8010000E /* The system could not dispose of the media in the requested manner. */ + ErrSCardProtoMismatch = 0x8010000F /* The requested protocols are incompatible with the protocol currently in use with the smart card. */ + ErrSCardNotReady = 0x80100010 /* The reader or smart card is not ready to accept commands. */ + ErrSCardInvalidValue = 0x80100011 /* One or more of the supplied parameters values could not be properly interpreted. */ + ErrSCardSystemCancelled = 0x80100012 /* The action was cancelled by the system, presumably to log off or shut down. */ + ErrSCardCommError = 0x80100013 /* An internal communications error has been detected. */ + ErrScardUnknownError = 0x80100014 /* An internal error has been detected, but the source is unknown. */ + ErrSCardInvalidATR = 0x80100015 /* An ATR obtained from the registry is not a valid ATR string. */ + ErrSCardNotTransacted = 0x80100016 /* An attempt was made to end a non-existent transaction. */ + ErrSCardReaderUnavailable = 0x80100017 /* The specified reader is not currently available for use. */ + ErrSCardShutdown = 0x80100018 /* The operation has been aborted to allow the server application to exit. */ + ErrSCardPCITooSmall = 0x80100019 /* The PCI Receive buffer was too small. */ + ErrSCardReaderUnsupported = 0x8010001A /* The reader driver does not meet minimal requirements for support. */ + ErrSCardDuplicateReader = 0x8010001B /* The reader driver did not produce a unique reader name. */ + ErrSCardCardUnsupported = 0x8010001C /* The smart card does not meet minimal requirements for support. */ + ErrScardNoService = 0x8010001D /* The Smart card resource manager is not running. */ + ErrSCardServiceStopped = 0x8010001E /* The Smart card resource manager has shut down. */ + ErrSCardUnexpected = 0x8010001F /* An unexpected card error has occurred. */ + ErrSCardUnsupportedFeature = 0x8010001F /* This smart card does not support the requested feature. */ + ErrSCardICCInstallation = 0x80100020 /* No primary provider can be found for the smart card. */ + ErrSCardICCCreateOrder = 0x80100021 /* The requested order of object creation is not supported. */ + ErrSCardDirNotFound = 0x80100023 /* The identified directory does not exist in the smart card. */ + ErrSCardFileNotFound = 0x80100024 /* The identified file does not exist in the smart card. */ + ErrSCardNoDir = 0x80100025 /* The supplied path does not represent a smart card directory. */ + ErrSCardNoFile = 0x80100026 /* The supplied path does not represent a smart card file. */ + ErrScardNoAccess = 0x80100027 /* Access is denied to this file. */ + ErrSCardWriteTooMany = 0x80100028 /* The smart card does not have enough memory to store the information. */ + ErrSCardBadSeek = 0x80100029 /* There was an error trying to set the smart card file object pointer. */ + ErrSCardInvalidCHV = 0x8010002A /* The supplied PIN is incorrect. */ + ErrSCardUnknownResMNG = 0x8010002B /* An unrecognized error code was returned from a layered component. */ + ErrSCardNoSuchCertificate = 0x8010002C /* The requested certificate does not exist. */ + ErrSCardCertificateUnavailable = 0x8010002D /* The requested certificate could not be obtained. */ + ErrSCardNoReadersAvailable = 0x8010002E /* Cannot find a smart card reader. */ + ErrSCardCommDataLost = 0x8010002F /* A communications error with the smart card has been detected. Retry the operation. */ + ErrScardNoKeyContainer = 0x80100030 /* The requested key container does not exist on the smart card. */ + ErrSCardServerTooBusy = 0x80100031 /* The Smart Card Resource Manager is too busy to complete this operation. */ + ErrSCardUnsupportedCard = 0x80100065 /* The reader cannot communicate with the card, due to ATR string configuration conflicts. */ + ErrSCardUnresponsiveCard = 0x80100066 /* The smart card is not responding to a reset. */ + ErrSCardUnpoweredCard = 0x80100067 /* Power has been removed from the smart card, so that further communication is not possible. */ + ErrSCardResetCard = 0x80100068 /* The smart card has been reset, so any shared state information is invalid. */ + ErrSCardRemovedCard = 0x80100069 /* The smart card has been removed, so further communication is not possible. */ + ErrSCardSecurityViolation = 0x8010006A /* Access was denied because of a security violation. */ + ErrSCardWrongCHV = 0x8010006B /* The card cannot be accessed because the wrong PIN was presented. */ + ErrSCardCHVBlocked = 0x8010006C /* The card cannot be accessed because the maximum number of PIN entry attempts has been reached. */ + ErrSCardEOF = 0x8010006D /* The end of the smart card file has been reached. */ + ErrSCardCancelledByUser = 0x8010006E /* The user pressed "Cancel" on a Smart Card Selection Dialog. */ + ErrSCardCardNotAuthenticated = 0x8010006F /* No PIN was presented to the smart card. */ ) // Code returns the error code, with an uint32 type to be used in PutUInt32 @@ -106,95 +106,95 @@ func (code ErrorCode) Code() uint32 { func (code ErrorCode) Error() error { switch code { case SCardSuccess: - return fmt.Errorf("command successful") + return fmt.Errorf("Command successful") case ErrSCardInternal: - return fmt.Errorf("internal error") + return fmt.Errorf("Internal error") case ErrSCardCancelled: - return fmt.Errorf("command cancelled") + return fmt.Errorf("Command cancelled") case ErrSCardInvalidHandle: - return fmt.Errorf("invalid handle") + return fmt.Errorf("Invalid handle") case ErrSCardInvalidParameter: - return fmt.Errorf("invalid parameter given") + return fmt.Errorf("Invalid parameter given") case ErrSCardInvalidTarget: - return fmt.Errorf("invalid target given") + return fmt.Errorf("Invalid target given") case ErrSCardNoMemory: - return fmt.Errorf("not enough memory") + return fmt.Errorf("Not enough memory") case ErrSCardWaitedTooLong: - return fmt.Errorf("waited too long") + return fmt.Errorf("Waited too long") case ErrSCardInsufficientBuffer: - return fmt.Errorf("insufficient buffer") + return fmt.Errorf("Insufficient buffer") case ErrScardUnknownReader: - return fmt.Errorf("unknown reader specified") + return fmt.Errorf("Unknown reader specified") case ErrSCardTimeout: - return fmt.Errorf("command timeout") + return fmt.Errorf("Command timeout") case ErrSCardSharingViolation: - return fmt.Errorf("sharing violation") + return fmt.Errorf("Sharing violation") case ErrSCardNoSmartCard: - return fmt.Errorf("no smart card inserted") + return fmt.Errorf("No smart card inserted") case ErrSCardUnknownCard: - return fmt.Errorf("unknown card") + return fmt.Errorf("Unknown card") case ErrSCardCannotDispose: - return fmt.Errorf("cannot dispose handle") + return fmt.Errorf("Cannot dispose handle") case ErrSCardProtoMismatch: - return fmt.Errorf("card protocol mismatch") + return fmt.Errorf("Card protocol mismatch") case ErrSCardNotReady: - return fmt.Errorf("subsystem not ready") + return fmt.Errorf("Subsystem not ready") case ErrSCardInvalidValue: - return fmt.Errorf("invalid value given") + return fmt.Errorf("Invalid value given") case ErrSCardSystemCancelled: - return fmt.Errorf("system cancelled") + return fmt.Errorf("System cancelled") case ErrSCardCommError: - return fmt.Errorf("rpc transport error") + return fmt.Errorf("RPC transport error") case ErrScardUnknownError: - return fmt.Errorf("unknown error") + return fmt.Errorf("Unknown error") case ErrSCardInvalidATR: - return fmt.Errorf("invalid ATR") + return fmt.Errorf("Invalid ATR") case ErrSCardNotTransacted: - return fmt.Errorf("transaction failed") + return fmt.Errorf("Transaction failed") case ErrSCardReaderUnavailable: - return fmt.Errorf("reader is unavailable") + return fmt.Errorf("Reader is unavailable") /* case SCARD_P_SHUTDOWN: */ case ErrSCardPCITooSmall: return fmt.Errorf("PCI struct too small") case ErrSCardReaderUnsupported: - return fmt.Errorf("reader is unsupported") + return fmt.Errorf("Reader is unsupported") case ErrSCardDuplicateReader: - return fmt.Errorf("reader already exists") + return fmt.Errorf("Reader already exists") case ErrSCardCardUnsupported: - return fmt.Errorf("card is unsupported") + return fmt.Errorf("Card is unsupported") case ErrScardNoService: - return fmt.Errorf("service not available") + return fmt.Errorf("Service not available") case ErrSCardServiceStopped: - return fmt.Errorf("service was stopped") + return fmt.Errorf("Service was stopped") /* case SCARD_E_UNEXPECTED: */ /* case SCARD_E_ICC_CREATEORDER: */ @@ -210,7 +210,7 @@ func (code ErrorCode) Error() error { /* case SCARD_E_NO_SUCH_CERTIFICATE: */ /* case SCARD_E_CERTIFICATE_UNAVAILABLE: */ case ErrSCardNoReadersAvailable: - return fmt.Errorf("cannot find a smart card reader") + return fmt.Errorf("Cannot find a smart card reader") /* case SCARD_E_COMM_DATA_LOST: */ /* case SCARD_E_NO_KEY_CONTAINER: */ @@ -238,7 +238,7 @@ func (code ErrorCode) Error() error { /* case SCARD_W_CARD_NOT_AUTHENTICATED: */ case ErrSCardUnsupportedFeature: - return fmt.Errorf("feature not supported") + return fmt.Errorf("Feature not supported") default: return fmt.Errorf("unknown error: %08x", code) diff --git a/vendor/github.com/gballet/go-libpcsclite/winscard.go b/vendor/github.com/gballet/go-libpcsclite/winscard.go index 791c974f1..b916db162 100644 --- a/vendor/github.com/gballet/go-libpcsclite/winscard.go +++ b/vendor/github.com/gballet/go-libpcsclite/winscard.go @@ -291,16 +291,16 @@ func (client *Client) Connect(name string, shareMode uint32, preferredProtocol u * * These data are passed throw the field \c sharedSegmentMsg.data. */ -//type transmit struct { -//hCard uint32 -//ioSendPciProtocol uint32 -//ioSendPciLength uint32 -//cbSendLength uint32 -//ioRecvPciProtocol uint32 -//ioRecvPciLength uint32 -//pcbRecvLength uint32 -//rv uint32 -//} +type transmit struct { + hCard uint32 + ioSendPciProtocol uint32 + ioSendPciLength uint32 + cbSendLength uint32 + ioRecvPciProtocol uint32 + ioRecvPciLength uint32 + pcbRecvLength uint32 + rv uint32 +} // SCardIoRequest contains the info needed for performing an IO request type SCardIoRequest struct { @@ -336,7 +336,7 @@ func (card *Card) Transmit(adpu []byte) ([]byte, *SCardIoRequest, error) { return nil, nil, err } if n != len(adpu) { - return nil, nil, fmt.Errorf("invalid number of bytes written: expected %d, got %d", len(adpu), n) + return nil, nil, fmt.Errorf("Invalid number of bytes written: expected %d, got %d", len(adpu), n) } response := [TransmitRequestLength]byte{} total := 0 diff --git a/vendor/github.com/golang-migrate/migrate/v4/.golangci.yml b/vendor/github.com/golang-migrate/migrate/v4/.golangci.yml index f447ee1ce..2d4d89158 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/.golangci.yml +++ b/vendor/github.com/golang-migrate/migrate/v4/.golangci.yml @@ -1,6 +1,6 @@ run: # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 2m + deadline: 2m linters: enable: #- golint diff --git a/vendor/github.com/golang-migrate/migrate/v4/.travis.yml b/vendor/github.com/golang-migrate/migrate/v4/.travis.yml index 72781d457..09183cc5b 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/.travis.yml +++ b/vendor/github.com/golang-migrate/migrate/v4/.travis.yml @@ -6,8 +6,8 @@ matrix: - go: master include: # Supported versions of Go: https://golang.org/dl/ + - go: "1.11.x" - go: "1.12.x" - - go: "1.13.x" - go: master go_import_path: github.com/golang-migrate/migrate @@ -34,7 +34,7 @@ before_install: - sudo apt-get update - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce # Install golangci-lint - - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.20.0 + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1 - echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}" install: @@ -57,7 +57,7 @@ deploy: secure: hWH1HLPpzpfA8pXQ93T1qKQVFSpQp0as/JLQ7D91jHuJ8p+RxVeqblDrR6HQY/95R/nyiE9GJmvUolSuw5h449LSrGxPtVWhdh6EnkxlQHlen5XeMhVjRjFV0sE9qGe8v7uAkiTfRO61ktTWHrEAvw5qpyqnNISodmZS78XIasPODQbNlzwINhWhDTHIjXGb4FpizYaL3OGCanrxfR9fQyCaqKGGBjRq3Mfq8U6Yd4mApmsE+uJxgaZV8K5zBqpkSzQRWhcVGNL5DuLsU3gfSJOo7kZeA2G71SHffH577dBoqtCZ4VFv169CoUZehLWCb+7XKJZmHXVujCURATSySLGUOPc6EoLFAn3YtsCA04mS4bZVo5FZPWVwfhjmkhtDR4f6wscKp7r1HsFHSOgm59QfETQdrn4MnZ44H2Jd39axqndn5DvK9EcZVjPHynOPnueXP2u6mTuUgh2VyyWBCDO3CNo0fGlo7VJI69IkIWNSD87K9cHZWYMClyKZkUzS+PmRAhHRYbVd+9ZjKOmnU36kUHNDG/ft1D4ogsY+rhVtXB4lgWDM5adri+EIScYdYnB1/pQexLBigcJY9uE7nQTR0U6QgVNYvun7uRNs40E0c4voSfmPdFO0FlOD2y1oQhnaXfWLbu9nMcTcs4RFGrcC7NzkUN4/WjG8s285V6w= skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true file: @@ -75,7 +75,7 @@ deploy: package_glob: '*.deb' skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true - provider: packagecloud @@ -87,7 +87,7 @@ deploy: package_glob: '*.deb' skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true - provider: packagecloud @@ -99,7 +99,7 @@ deploy: package_glob: '*.deb' skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true - provider: packagecloud @@ -111,7 +111,7 @@ deploy: package_glob: '*.deb' skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true - provider: packagecloud @@ -123,13 +123,13 @@ deploy: package_glob: '*.deb' skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true - provider: script script: ./docker-deploy.sh skip_cleanup: true on: - go: "1.13.x" + go: "1.12.x" repo: golang-migrate/migrate tags: true diff --git a/vendor/github.com/golang-migrate/migrate/v4/Dockerfile b/vendor/github.com/golang-migrate/migrate/v4/Dockerfile index cfab35537..cb11b0b2e 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/Dockerfile +++ b/vendor/github.com/golang-migrate/migrate/v4/Dockerfile @@ -8,7 +8,7 @@ WORKDIR /go/src/github.com/golang-migrate/migrate COPY . ./ ENV GO111MODULE=on -ENV DATABASES="postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver firebird" +ENV DATABASES="postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver" ENV SOURCES="file go_bindata github github_ee aws_s3 google_cloud_storage godoc_vfs gitlab" RUN go build -a -o build/migrate.linux-386 -ldflags="-s -w -X main.Version=${VERSION}" -tags "$DATABASES $SOURCES" ./cmd/migrate diff --git a/vendor/github.com/golang-migrate/migrate/v4/Makefile b/vendor/github.com/golang-migrate/migrate/v4/Makefile index 291c179cc..a0cd24afe 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/Makefile +++ b/vendor/github.com/golang-migrate/migrate/v4/Makefile @@ -1,5 +1,5 @@ SOURCE ?= file go_bindata github github_ee aws_s3 google_cloud_storage godoc_vfs gitlab -DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver firebird +DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-) TEST_FLAGS ?= REPO_OWNER ?= $(shell cd .. && basename "$$(pwd)") diff --git a/vendor/github.com/golang-migrate/migrate/v4/README.md b/vendor/github.com/golang-migrate/migrate/v4/README.md index 2f8af4de2..facd3c39f 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/README.md +++ b/vendor/github.com/golang-migrate/migrate/v4/README.md @@ -3,7 +3,7 @@ [![Coverage Status](https://img.shields.io/coveralls/github/golang-migrate/migrate/master.svg)](https://coveralls.io/github/golang-migrate/migrate?branch=master) [![packagecloud.io](https://img.shields.io/badge/deb-packagecloud.io-844fec.svg)](https://packagecloud.io/golang-migrate/migrate?filter=debs) [![Docker Pulls](https://img.shields.io/docker/pulls/migrate/migrate.svg)](https://hub.docker.com/r/migrate/migrate/) -![Supported Go Versions](https://img.shields.io/badge/Go-1.12%2C%201.13-lightgrey.svg) +![Supported Go Versions](https://img.shields.io/badge/Go-1.11%2C%201.12-lightgrey.svg) [![GitHub Release](https://img.shields.io/github/release/golang-migrate/migrate.svg)](https://github.com/golang-migrate/migrate/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/golang-migrate/migrate)](https://goreportcard.com/report/github.com/golang-migrate/migrate) @@ -36,7 +36,7 @@ Database drivers run migrations. [Add a new database?](database/driver.go) * [Google Cloud Spanner](database/spanner) * [CockroachDB](database/cockroachdb) * [ClickHouse](database/clickhouse) -* [Firebird](database/firebird) +* [Firebird](database/firebird) ([todo #49](https://github.com/golang-migrate/migrate/issues/49)) * [MS SQL Server](database/sqlserver) ### Database URLs diff --git a/vendor/github.com/golang-migrate/migrate/v4/go.mod b/vendor/github.com/golang-migrate/migrate/v4/go.mod index 41ed143d4..7729edcc2 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/go.mod +++ b/vendor/github.com/golang-migrate/migrate/v4/go.mod @@ -54,5 +54,3 @@ require ( google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb google.golang.org/grpc v1.20.1 // indirect ) - -go 1.12 diff --git a/vendor/github.com/gomarkdown/markdown/ast/node.go b/vendor/github.com/gomarkdown/markdown/ast/node.go index a6f0327c7..9b3f4a920 100644 --- a/vendor/github.com/gomarkdown/markdown/ast/node.go +++ b/vendor/github.com/gomarkdown/markdown/ast/node.go @@ -348,6 +348,21 @@ func (c *StatusTag) MarshalJSON() ([]byte, error) { return json.Marshal(&c1) } +type Mention struct { + Leaf +} + +func (c *Mention) MarshalJSON() ([]byte, error) { + type MentionJSON struct { + Type string `json:"type"` + Literal string `json:"literal"` + } + var c1 MentionJSON + c1.Literal = string(c.Literal) + c1.Type = "mention" + return json.Marshal(&c1) +} + // Strong represents markdown strong node type Strong struct { Leaf diff --git a/vendor/github.com/gomarkdown/markdown/parser/inline.go b/vendor/github.com/gomarkdown/markdown/parser/inline.go index 1c416617d..3eef2a943 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/inline.go +++ b/vendor/github.com/gomarkdown/markdown/parser/inline.go @@ -63,6 +63,41 @@ func (p *Parser) Inline(currBlock ast.Node, data []byte) { p.nesting-- } +const pkLength = 132 + +func mention(p *Parser, data []byte, offset int) (int, ast.Node) { + data = data[offset:] + n := len(data) + + if n < pkLength+1 { + return 0, nil + } + + // need to start with 0x + if data[1] != '0' || data[2] != 'x' { + + return 0, nil + } + + i := 3 + for i < pkLength+1 { + if !isValidPublicKeyChar(data[i]) { + return 0, nil + } + i++ + } + + // Check there's a space + if n != pkLength+1 && !isValidTerminatingMentionChar(data[pkLength+1]) { + return 0, nil + } + + mention := &ast.Mention{} + mention.Literal = data[1 : pkLength+1] + + return i, mention +} + func statusTag(p *Parser, data []byte, offset int) (int, ast.Node) { data = data[offset:] n := len(data) diff --git a/vendor/github.com/gomarkdown/markdown/parser/parser.go b/vendor/github.com/gomarkdown/markdown/parser/parser.go index b87d5b605..900b699fb 100644 --- a/vendor/github.com/gomarkdown/markdown/parser/parser.go +++ b/vendor/github.com/gomarkdown/markdown/parser/parser.go @@ -143,6 +143,7 @@ func NewWithExtensions(extension Extensions) *Parser { p.inlineCallback[' '] = maybeLineBreak p.inlineCallback['*'] = emphasis p.inlineCallback['#'] = statusTag + p.inlineCallback['@'] = mention p.inlineCallback['_'] = emphasis if p.extensions&Strikethrough != 0 { p.inlineCallback['~'] = emphasis @@ -684,6 +685,14 @@ func isValidStatusTagChar(c byte) bool { return isAlnum(c) || c == '-' } +func isValidTerminatingMentionChar(c byte) bool { + return isSpace(c) || c == '.' || c == ',' || c == ':' || c == ';' +} + +func isValidPublicKeyChar(c byte) bool { + return c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9' || c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f' +} + // TODO: this is not used // Replace tab characters with spaces, aligning to the next TAB_SIZE column. // always ends output with a newline diff --git a/vendor/github.com/status-im/status-go/protocol/chat.go b/vendor/github.com/status-im/status-go/protocol/chat.go index 0e5e04b21..684e1cea8 100644 --- a/vendor/github.com/status-im/status-go/protocol/chat.go +++ b/vendor/github.com/status-im/status-go/protocol/chat.go @@ -5,12 +5,23 @@ import ( "crypto/sha1" "encoding/hex" "encoding/json" + "math/rand" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) +var chatColors = []string{ + "#fa6565", // red + "#887af9", // blue + "#FE8F59", // orange + "#7cda00", // green + "#51d0f0", // light-blue + "#d37ef4", // purple +} + type ChatType int const ( @@ -31,9 +42,6 @@ type Chat struct { ChatType ChatType `json:"chatType"` - // Only filled for one to one chats - PublicKey *ecdsa.PublicKey `json:"-"` - // Timestamp indicates the last time this chat has received/sent a message Timestamp int64 `json:"timestamp"` // LastClockValue indicates the last clock value to be used when sending messages @@ -50,7 +58,21 @@ type Chat struct { // Members are the members who have been invited to the group chat Members []ChatMember `json:"members"` // MembershipUpdates is all the membership events in the chat - MembershipUpdates []ChatMembershipUpdate `json:"membershipUpdates"` + MembershipUpdates []v1protocol.MembershipUpdateEvent `json:"membershipUpdateEvents"` +} + +func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) { + // For one to one chatID is an encoded public key + if c.ChatType != ChatTypeOneToOne { + return nil, nil + } + pkey, err := hex.DecodeString(c.ID[2:]) + if err != nil { + return nil, err + } + // Safety check, make sure is well formed + return crypto.UnmarshalPubkey(pkey) + } func (c *Chat) MarshalJSON() ([]byte, error) { @@ -131,30 +153,15 @@ func (c *Chat) updateChatFromProtocolGroup(g *v1protocol.Group) { c.Members = chatMembers // MembershipUpdates - updates := g.Updates() - membershipUpdates := make([]ChatMembershipUpdate, 0, len(updates)) - for _, update := range updates { - membershipUpdate := ChatMembershipUpdate{ - Type: update.Type, - Name: update.Name, - ClockValue: uint64(update.ClockValue), // TODO: get rid of type casting - Signature: update.Signature, - From: update.From, - Member: update.Member, - Members: update.Members, - } - membershipUpdate.setID() - membershipUpdates = append(membershipUpdates, membershipUpdate) - } - c.MembershipUpdates = membershipUpdates + c.MembershipUpdates = g.Events() } // ChatMembershipUpdate represent an event on membership of the chat type ChatMembershipUpdate struct { // Unique identifier for the event ID string `json:"id"` - // Type indicates the kind of event (i.e changed-name, added-member, etc) - Type string `json:"type"` + // Type indicates the kind of event + Type protobuf.MembershipUpdateEvent_EventType `json:"type"` // Name represents the name in the event of changing name events Name string `json:"name,omitempty"` // Clock value of the event @@ -198,11 +205,10 @@ func oneToOneChatID(publicKey *ecdsa.PublicKey) string { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey) Chat { return Chat{ - ID: oneToOneChatID(publicKey), - Name: name, - Active: true, - ChatType: ChatTypeOneToOne, - PublicKey: publicKey, + ID: oneToOneChatID(publicKey), + Name: name, + Active: true, + ChatType: ChatTypeOneToOne, } } @@ -211,14 +217,17 @@ func CreatePublicChat(name string) Chat { ID: name, Name: name, Active: true, + Color: chatColors[rand.Intn(len(chatColors))], ChatType: ChatTypePublic, } } func createGroupChat() Chat { return Chat{ - Active: true, - ChatType: ChatTypePrivateGroupChat, + Active: true, + Color: chatColors[rand.Intn(len(chatColors))], + Timestamp: int64(timestampInMs()), + ChatType: ChatTypePrivateGroupChat, } } diff --git a/vendor/github.com/status-im/status-go/protocol/chat_group_proxy.go b/vendor/github.com/status-im/status-go/protocol/chat_group_proxy.go index f31fe623d..5e2c44964 100644 --- a/vendor/github.com/status-im/status-go/protocol/chat_group_proxy.go +++ b/vendor/github.com/status-im/status-go/protocol/chat_group_proxy.go @@ -5,24 +5,5 @@ import ( ) func newProtocolGroupFromChat(chat *Chat) (*v1protocol.Group, error) { - return v1protocol.NewGroup(chat.ID, chatToFlattenMembershipUpdate(chat)) -} - -func chatToFlattenMembershipUpdate(chat *Chat) []v1protocol.MembershipUpdateFlat { - result := make([]v1protocol.MembershipUpdateFlat, len(chat.MembershipUpdates)) - for idx, update := range chat.MembershipUpdates { - result[idx] = v1protocol.MembershipUpdateFlat{ - ChatID: chat.ID, - From: update.From, - Signature: update.Signature, - MembershipUpdateEvent: v1protocol.MembershipUpdateEvent{ - Name: update.Name, - Type: update.Type, - ClockValue: int64(update.ClockValue), // TODO: remove type difference - Member: update.Member, - Members: update.Members, - }, - } - } - return result + return v1protocol.NewGroup(chat.ID, chat.MembershipUpdates) } diff --git a/vendor/github.com/status-im/status-go/protocol/go.mod b/vendor/github.com/status-im/status-go/protocol/go.mod index 455f92473..ca0400c3c 100644 --- a/vendor/github.com/status-im/status-go/protocol/go.mod +++ b/vendor/github.com/status-im/status-go/protocol/go.mod @@ -4,7 +4,7 @@ go 1.13 replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7 -replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 +replace github.com/gomarkdown/markdown => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/eth-node => ../eth-node @@ -14,20 +14,18 @@ require ( github.com/cenkalti/backoff/v3 v3.0.0 github.com/ethereum/go-ethereum v1.9.5 github.com/golang/protobuf v1.3.2 - github.com/gomarkdown/markdown v0.0.0-20191113114344-af599402d015 + github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba github.com/google/uuid v1.1.1 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/lucasb-eyer/go-colorful v1.0.2 - github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f github.com/pkg/errors v0.8.1 github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a github.com/status-im/doubleratchet v3.0.0+incompatible github.com/status-im/migrate/v4 v4.6.2-status.2 - github.com/status-im/status-go/eth-node v0.0.0-20191120100713-5053b0b6835b - github.com/status-im/status-go/whisper/v6 v6.0.0 + github.com/status-im/status-go/eth-node v1.0.0 + github.com/status-im/status-go/whisper/v6 v6.0.1 github.com/stretchr/testify v1.4.0 github.com/vacp2p/mvds v0.0.23 go.uber.org/zap v1.13.0 - golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba ) diff --git a/vendor/github.com/status-im/status-go/protocol/go.sum b/vendor/github.com/status-im/status-go/protocol/go.sum index 7b6fcca1a..7886435ed 100644 --- a/vendor/github.com/status-im/status-go/protocol/go.sum +++ b/vendor/github.com/status-im/status-go/protocol/go.sum @@ -34,9 +34,8 @@ github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1: github.com/aristanetworks/goarista v0.0.0-20181002214814-33151c4543a7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20190219163901-728bce664cf5 h1:L0TwgZQo7Mga9im6FvKEZGIvyLE/VG/HI5loz5LpvC0= github.com/aristanetworks/goarista v0.0.0-20190219163901-728bce664cf5/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 h1:tS7jSmwRqSxTnonTRlDD1oHo6Q9YOK4xHS9/v4L56eg= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= -github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 h1:fKnuvQ/O22ZpD7HaJjGQXn/GxOdDJOQFL8bpM8Xe3X8= -github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= @@ -49,6 +48,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190418232430-6867ff32788a/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -124,6 +125,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elastic/gosigar v0.10.4 h1:6jfw75dsoflhBMRdO6QPzQUgLqUYTsQQQRkkcsHsuPo= github.com/elastic/gosigar v0.10.4/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo= +github.com/elastic/gosigar v0.10.5/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= @@ -136,6 +139,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= @@ -145,10 +150,10 @@ github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2i github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -160,6 +165,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-migrate/migrate/v4 v4.6.2 h1:LDDOHo/q1W5UDj6PbkxdCv7lv9yunyZHXvxuwDkGo3k= github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= +github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs= +github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -254,6 +261,8 @@ github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2vi github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8 h1:VhnqxaTIudc9IWKx8uXRLnpdSb9noCEj+vHacjmhp68= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 h1:ZHuwnjpP8LsVsUYqTqeVAI+GfDfJ6UNPrExZF+vX/DQ= +github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -270,8 +279,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -359,8 +368,14 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5 h1:l16XLUUJ34wIz+RIvLhSwGvLvKyy+W598b135bJN6mg= github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -373,8 +388,26 @@ github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVq github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5 h1:1wxmCvTXAifAepIMyF39vZinRw5sbqjPs/UIi93+uik= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f h1:hd3r+uv9DNLScbOrnlj82rBldHQf3XWmCeXAWbw8euQ= github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f/go.mod h1:MyUWrZlB1aI5bs7j9/pJ8ckLLZ4QcCYcNiSbsAW32D4= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -480,21 +513,27 @@ github.com/status-im/doubleratchet v3.0.0+incompatible h1:aJ1ejcSERpSzmWZBgtfYti github.com/status-im/doubleratchet v3.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU= github.com/status-im/go-ethereum v1.9.5-status.6 h1:ytuTO1yBIAuTVRtRQoc2mrdyngtP+XOQ9IHIibbz7/I= github.com/status-im/go-ethereum v1.9.5-status.6/go.mod h1:08JvQWE+IOnAFSe4UD4ACLNe2fDd9XmWMCq5Yzy9mk0= +github.com/status-im/go-ethereum v1.9.5-status.7 h1:DKH1GiF52LwaZaw6YDBliFEgm/JDsbIT+hn7ph6X94Q= github.com/status-im/go-ethereum v1.9.5-status.7/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 h1:ijC73VP0hucsy/MRn4cmtoQVB1mKdLcvurMYPvmPc4Y= -github.com/status-im/markdown v0.0.0-20191113114344-af599402d015/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= +github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0 h1:5UdlDkkBoPrJfh7zkfoR3X5utJhNs/MCQysK3x0ycgg= +github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba h1:Ut2CKuG+L9eWFL7dTEPuLE+RKecUYBkDNhqXSIgY92U= +github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8= github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= +github.com/status-im/rendezvous v1.3.0/go.mod h1:+hzjuP+j/XzLPeF6E50b88pWOTLdTcwjvNYt+Gh1W1s= github.com/status-im/status-go v0.36.0 h1:91qDMJjHv+T3Li9FwxsWQ2JBVcYtvVDT0nGFSMnmM+8= github.com/status-im/status-go v0.36.1 h1:nb9eTq0UQJ57YyTZSl5U05emFT+R4AW8/Bga6ocgOks= +github.com/status-im/status-go v0.37.3 h1:94/bOA8qrEIgWd23mSLN39SwUJwCu2TPQFV2HzSI2ZE= +github.com/status-im/status-go v0.37.3/go.mod h1:9qHQ2+8NS6ivPJS5YbsI3gWkr0t6DWmJzKnr4M7vudw= github.com/status-im/status-go/extkeys v1.0.0 h1:Qyirsoi5Ye5UFfisgPtCjPb/RkBxyK+UsSiEcr2PVlM= github.com/status-im/status-go/extkeys v1.0.0/go.mod h1:GdqJbrcpkNm5ZsSCpp+PdMxnXx+OcRBdm3PI0rs1FpU= +github.com/status-im/status-go/protocol v1.0.1/go.mod h1:LpA7BsaNmj6EOdq7BwuqncewjPqIRHCletZOb2wlWrY= +github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= github.com/status-im/whisper v1.5.2 h1:26NgiKusmPic38eQdtXnaY+iaQ/LuQ3Dh0kCGYT/Uxs= github.com/status-im/whisper v1.5.2/go.mod h1:emrOxzJme0k66QtbbQ2bdd3P8RCdLZ8sTD7SkwH1s2s= -github.com/status-im/whisper v1.6.1 h1:C/T1HQHZfUI2jbccf3yIe8yfkl435I3BILIKeNASJDc= -github.com/status-im/whisper v1.6.1/go.mod h1:lygchT4p9Y1/hR451OhNNqfinvy9EYEDxtXU2T/U30Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= @@ -503,6 +542,7 @@ github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9C github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -604,8 +644,11 @@ golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -616,6 +659,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -629,8 +674,15 @@ golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk= +golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= diff --git a/vendor/github.com/status-im/status-go/protocol/group_chat_system_messages.go b/vendor/github.com/status-im/status-go/protocol/group_chat_system_messages.go new file mode 100644 index 000000000..cbbd7260d --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/group_chat_system_messages.go @@ -0,0 +1,88 @@ +package protocol + +import ( + "fmt" + "strings" + "time" + + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/protobuf" + v1protocol "github.com/status-im/status-go/protocol/v1" +) + +var defaultSystemMessagesTranslations = map[protobuf.MembershipUpdateEvent_EventType]string{ + protobuf.MembershipUpdateEvent_CHAT_CREATED: "{{from}} created the group {{name}}", + protobuf.MembershipUpdateEvent_NAME_CHANGED: "{{from}} changed the group's name to {{name}}", + protobuf.MembershipUpdateEvent_MEMBERS_ADDED: "{{from}} has invited {{members}}", + protobuf.MembershipUpdateEvent_MEMBER_JOINED: "{{from}} joined the group", + protobuf.MembershipUpdateEvent_ADMINS_ADDED: "{{from}} has made {{members}} admin", + protobuf.MembershipUpdateEvent_MEMBER_REMOVED: "{{member}} left the group", + protobuf.MembershipUpdateEvent_ADMIN_REMOVED: "{{member}} is not admin anymore", +} + +func tsprintf(format string, params map[string]string) string { + for key, val := range params { + format = strings.Replace(format, "{{"+key+"}}", fmt.Sprintf("%s", val), -1) + } + return fmt.Sprintf(format) +} + +func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) *Message { + var text string + switch e.Type { + case protobuf.MembershipUpdateEvent_CHAT_CREATED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_CHAT_CREATED], map[string]string{"from": "@" + e.From, "name": e.Name}) + case protobuf.MembershipUpdateEvent_NAME_CHANGED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_NAME_CHANGED], map[string]string{"from": "@" + e.From, "name": e.Name}) + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: + + var memberMentions []string + for _, s := range e.Members { + memberMentions = append(memberMentions, "@"+s) + } + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBERS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")}) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_JOINED], map[string]string{"from": "@" + e.From}) + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: + var memberMentions []string + for _, s := range e.Members { + memberMentions = append(memberMentions, "@"+s) + } + text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMINS_ADDED], map[string]string{"from": "@" + e.From, "members": strings.Join(memberMentions, ", ")}) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_MEMBER_REMOVED], map[string]string{"member": "@" + e.Members[0]}) + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + text = tsprintf(translations[protobuf.MembershipUpdateEvent_ADMIN_REMOVED], map[string]string{"member": "@" + e.Members[0]}) + + } + timestamp := v1protocol.TimestampInMsFromTime(time.Now()) + message := &Message{ + ChatMessage: protobuf.ChatMessage{ + ChatId: e.ChatID, + Text: text, + MessageType: protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP, + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + Clock: e.ClockValue, + Timestamp: timestamp, + }, + From: e.From, + WhisperTimestamp: timestamp, + LocalChatID: e.ChatID, + Seen: true, + ID: types.EncodeHex(crypto.Keccak256(e.Signature)), + } + message.PrepareContent() + return message +} + +func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*Message { + var messages []*Message + + for _, e := range events { + messages = append(messages, eventToSystemMessage(e, translations)) + + } + + return messages +} diff --git a/vendor/github.com/status-im/status-go/protocol/message_handler.go b/vendor/github.com/status-im/status-go/protocol/message_handler.go index b34c21a48..171ad0b0c 100644 --- a/vendor/github.com/status-im/status-go/protocol/message_handler.go +++ b/vendor/github.com/status-im/status-go/protocol/message_handler.go @@ -3,66 +3,44 @@ package protocol import ( "github.com/pkg/errors" + "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) -type persistentMessageHandler struct { - persistence *sqlitePersistence -} - -func newPersistentMessageHandler(persistence *sqlitePersistence) *persistentMessageHandler { - return &persistentMessageHandler{persistence: persistence} -} - // HandleMembershipUpdate updates a Chat instance according to the membership updates. // It retrieves chat, if exists, and merges membership updates from the message. // Finally, the Chat is updated with the new group events. -func (h *persistentMessageHandler) HandleMembershipUpdate(m v1protocol.MembershipUpdateMessage) error { - chat, err := h.chatID(m.ChatID) - switch err { - case errChatNotFound: - group, err := v1protocol.NewGroupWithMembershipUpdates(m.ChatID, m.Updates) +func HandleMembershipUpdate(chat *Chat, m *v1protocol.MembershipUpdateMessage, myIdentity string, translations map[protobuf.MembershipUpdateEvent_EventType]string) (*Chat, []*Message, error) { + if chat == nil { + if len(m.Events) == 0 { + return nil, nil, errors.New("can't create new group chat without events") + } + group, err := v1protocol.NewGroupWithEvents(m.ChatID, m.Events) if err != nil { - return err + return nil, nil, err + } + + // A new chat must contain us + if !group.IsMember(myIdentity) { + return nil, nil, errors.New("can't create a new group chat without us being a member") } newChat := createGroupChat() newChat.updateChatFromProtocolGroup(group) - chat = &newChat - case nil: - existingGroup, err := newProtocolGroupFromChat(chat) - if err != nil { - return errors.Wrap(err, "failed to create a Group from Chat") - } - updateGroup, err := v1protocol.NewGroupWithMembershipUpdates(m.ChatID, m.Updates) - if err != nil { - return errors.Wrap(err, "invalid membership update") - } - merged := v1protocol.MergeFlatMembershipUpdates(existingGroup.Updates(), updateGroup.Updates()) - newGroup, err := v1protocol.NewGroup(chat.ID, merged) - if err != nil { - return errors.Wrap(err, "failed to create a group with new membership updates") - } - chat.updateChatFromProtocolGroup(newGroup) - default: - return err + return &newChat, buildSystemMessages(m.Events, translations), nil } - return h.persistence.SaveChat(*chat) -} - -func (h *persistentMessageHandler) chatID(chatID string) (*Chat, error) { - var chat *Chat - chats, err := h.persistence.Chats() + existingGroup, err := newProtocolGroupFromChat(chat) if err != nil { - return nil, err + return nil, nil, errors.Wrap(err, "failed to create a Group from Chat") } - for _, ch := range chats { - if ch.ID == chatID { - chat = ch - break - } + updateGroup, err := v1protocol.NewGroupWithEvents(m.ChatID, m.Events) + if err != nil { + return nil, nil, errors.Wrap(err, "invalid membership update") } - if chat == nil { - return nil, errChatNotFound + merged := v1protocol.MergeMembershipUpdateEvents(existingGroup.Events(), updateGroup.Events()) + newGroup, err := v1protocol.NewGroup(chat.ID, merged) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create a group with new membership updates") } - return chat, nil + chat.updateChatFromProtocolGroup(newGroup) + return chat, buildSystemMessages(m.Events, translations), nil } diff --git a/vendor/github.com/status-im/status-go/protocol/message_processor.go b/vendor/github.com/status-im/status-go/protocol/message_processor.go index 04850332c..051708fef 100644 --- a/vendor/github.com/status-im/status-go/protocol/message_processor.go +++ b/vendor/github.com/status-im/status-go/protocol/message_processor.go @@ -14,6 +14,7 @@ import ( datasyncpeer "github.com/status-im/status-go/protocol/datasync/peer" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/encryption/multidevice" + "github.com/status-im/status-go/protocol/protobuf" transport "github.com/status-im/status-go/protocol/transport/whisper" v1protocol "github.com/status-im/status-go/protocol/v1" datasyncnode "github.com/vacp2p/mvds/node" @@ -28,16 +29,11 @@ const ( whisperPoWTime = 5 ) -type messageHandler interface { - HandleMembershipUpdate(m v1protocol.MembershipUpdateMessage) error -} - type messageProcessor struct { identity *ecdsa.PrivateKey datasync *datasync.DataSync protocol *encryption.Protocol transport *transport.WhisperServiceTransport - handler messageHandler logger *zap.Logger featureFlags featureFlags @@ -48,7 +44,6 @@ func newMessageProcessor( database *sql.DB, enc *encryption.Protocol, transport *transport.WhisperServiceTransport, - handler messageHandler, logger *zap.Logger, features featureFlags, ) (*messageProcessor, error) { @@ -71,7 +66,6 @@ func newMessageProcessor( datasync: ds, protocol: enc, transport: transport, - handler: handler, logger: logger, featureFlags: features, } @@ -97,13 +91,14 @@ func (p *messageProcessor) SendPrivateRaw( ctx context.Context, recipient *ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug( "sending a private message", zap.Binary("public-key", crypto.FromECDSAPub(recipient)), zap.String("site", "SendPrivateRaw"), ) - return p.sendPrivate(ctx, recipient, data) + return p.sendPrivate(ctx, recipient, data, messageType) } // SendGroupRaw takes encoded data, encrypts it and sends through the wire, @@ -112,13 +107,14 @@ func (p *messageProcessor) SendGroupRaw( ctx context.Context, recipients []*ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug( "sending a private group message", zap.String("site", "SendGroupRaw"), ) // Calculate messageID first - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -126,7 +122,7 @@ func (p *messageProcessor) SendGroupRaw( messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) for _, recipient := range recipients { - _, err = p.sendPrivate(ctx, recipient, data) + _, err = p.sendPrivate(ctx, recipient, data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to send message") } @@ -139,10 +135,11 @@ func (p *messageProcessor) sendPrivate( ctx context.Context, recipient *ecdsa.PublicKey, data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, ) ([]byte, error) { p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient))) - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -176,37 +173,34 @@ func (p *messageProcessor) sendPrivate( func (p *messageProcessor) SendMembershipUpdate( ctx context.Context, recipients []*ecdsa.PublicKey, - chatID string, - updates []v1protocol.MembershipUpdate, - clock int64, -) ([][]byte, error) { + group *v1protocol.Group, + chatMessage *protobuf.ChatMessage, +) ([]byte, error) { p.logger.Debug("sending a membership update", zap.Int("membersCount", len(recipients))) message := v1protocol.MembershipUpdateMessage{ - ChatID: chatID, - Updates: updates, + ChatID: group.ChatID(), + Events: group.Events(), + Message: chatMessage, } encodedMessage, err := v1protocol.EncodeMembershipUpdateMessage(message) if err != nil { return nil, errors.Wrap(err, "failed to encode membership update message") } - var resultIDs [][]byte - for _, recipient := range recipients { - messageID, err := p.sendPrivate(ctx, recipient, encodedMessage) - if err != nil { - return nil, err - } - resultIDs = append(resultIDs, messageID) - } - return resultIDs, nil + return p.SendGroupRaw(ctx, recipients, encodedMessage, protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE) } // SendPublicRaw takes encoded data, encrypts it and sends through the wire. -func (p *messageProcessor) SendPublicRaw(ctx context.Context, chatName string, data []byte) ([]byte, error) { +func (p *messageProcessor) SendPublicRaw( + ctx context.Context, + chatName string, + data []byte, + messageType protobuf.ApplicationMetadataMessage_Type, +) ([]byte, error) { var newMessage *types.NewMessage - wrappedMessage, err := p.wrapMessageV1(data) + wrappedMessage, err := p.wrapMessageV1(data, messageType) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -230,16 +224,6 @@ func (p *messageProcessor) SendPublicRaw(ctx context.Context, chatName string, d return messageID, nil } -func (p *messageProcessor) processMembershipUpdate(m v1protocol.MembershipUpdateMessage) error { - if err := m.Verify(); err != nil { - return err - } - if p.handler != nil { - return p.handler.HandleMembershipUpdate(m) - } - return errors.New("missing handler") -} - func (p *messageProcessor) processPairMessage(m v1protocol.PairMessage) error { metadata := &multidevice.InstallationMetadata{ Name: m.Name, @@ -336,8 +320,8 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe return nil } -func (p *messageProcessor) wrapMessageV1(encodedMessage []byte) ([]byte, error) { - wrappedMessage, err := v1protocol.WrapMessageV1(encodedMessage, p.identity) +func (p *messageProcessor) wrapMessageV1(encodedMessage []byte, messageType protobuf.ApplicationMetadataMessage_Type) ([]byte, error) { + wrappedMessage, err := v1protocol.WrapMessageV1(encodedMessage, messageType, p.identity) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } diff --git a/vendor/github.com/status-im/status-go/protocol/message_validator.go b/vendor/github.com/status-im/status-go/protocol/message_validator.go new file mode 100644 index 000000000..7c1bd5623 --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/message_validator.go @@ -0,0 +1,51 @@ +package protocol + +import ( + "errors" + "github.com/status-im/status-go/protocol/protobuf" + "strings" +) + +func ValidateReceivedChatMessage(message *protobuf.ChatMessage) error { + if message.Clock == 0 { + return errors.New("Clock can't be 0") + } + + if message.Timestamp == 0 { + return errors.New("Timestamp can't be 0") + } + + if len(strings.TrimSpace(message.Text)) == 0 { + return errors.New("Text can't be empty") + } + + if len(message.ChatId) == 0 { + return errors.New("ChatId can't be empty") + } + + if message.ContentType == protobuf.ChatMessage_UNKNOWN_CONTENT_TYPE { + return errors.New("Unknown content type") + } + + if message.MessageType == protobuf.ChatMessage_UNKNOWN_MESSAGE_TYPE || message.MessageType == protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP { + return errors.New("Unknown message type") + } + + if message.ContentType == protobuf.ChatMessage_STICKER { + if message.Payload == nil { + return errors.New("No sticker content") + } + sticker := message.GetSticker() + if sticker == nil { + return errors.New("No sticker content") + } + + if sticker.Pack == 0 { + return errors.New("Sticker pack not set") + } + if len(sticker.Hash) == 0 { + return errors.New("Sticker hash not set") + } + } + return nil +} diff --git a/vendor/github.com/status-im/status-go/protocol/messenger.go b/vendor/github.com/status-im/status-go/protocol/messenger.go index 594b122ba..b0cf326d5 100644 --- a/vendor/github.com/status-im/status-go/protocol/messenger.go +++ b/vendor/github.com/status-im/status-go/protocol/messenger.go @@ -6,7 +6,8 @@ import ( "database/sql" "encoding/hex" "encoding/json" - "strconv" + "math/rand" + "sync" "time" "github.com/pkg/errors" @@ -44,17 +45,20 @@ var ( // Similarly, it needs to expose an interface to manage // mailservers because they can also be managed by the user. type Messenger struct { - node types.Node - identity *ecdsa.PrivateKey - persistence *sqlitePersistence - transport *transport.WhisperServiceTransport - encryptor *encryption.Protocol - processor *messageProcessor - logger *zap.Logger - + node types.Node + identity *ecdsa.PrivateKey + persistence *sqlitePersistence + transport *transport.WhisperServiceTransport + encryptor *encryption.Protocol + processor *messageProcessor + logger *zap.Logger featureFlags featureFlags messagesPersistenceEnabled bool shutdownTasks []func() error + systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string + allChats map[string]*Chat + allContacts map[string]*Contact + mutex sync.Mutex } type RawResponse struct { @@ -95,6 +99,8 @@ type config struct { // DEPRECATED: no need to expose it onSendContactCodeHandler func(*encryption.ProtocolMessageSpec) + // systemMessagesTranslations holds translations for system-messages + systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string // Config for the envelopes monitor envelopesMonitorConfig *transport.EnvelopesMonitorConfig @@ -118,6 +124,13 @@ func WithOnNewInstallationsHandler(h func([]*multidevice.Installation)) Option { } } +func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { + return func(c *config) error { + c.systemMessagesTranslations = t + return nil + } +} + func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option { return func(c *config) error { c.onNegotiatedFilters = h @@ -222,7 +235,7 @@ func NewMessenger( if c.onSendContactCodeHandler == nil { c.onSendContactCodeHandler = func(messageSpec *encryption.ProtocolMessageSpec) { slogger := logger.With(zap.String("site", "onSendContactCodeHandler")) - slogger.Info("received a SendContactCode request") + slogger.Debug("received a SendContactCode request") newMessage, err := messageSpecToWhisper(messageSpec) if err != nil { @@ -240,6 +253,10 @@ func NewMessenger( } } + if c.systemMessagesTranslations == nil { + c.systemMessagesTranslations = defaultSystemMessagesTranslations + } + // Configure the database. database := c.db if c.db == nil && c.dbConfig == (dbConfig{}) { @@ -288,7 +305,6 @@ func NewMessenger( database, encryptionProtocol, t, - newPersistentMessageHandler(&sqlitePersistence{db: database}), logger, c.featureFlags, ) @@ -304,6 +320,9 @@ func NewMessenger( encryptor: encryptionProtocol, processor: processor, featureFlags: c.featureFlags, + systemMessagesTranslations: c.systemMessagesTranslations, + allChats: make(map[string]*Chat), + allContacts: make(map[string]*Contact), messagesPersistenceEnabled: c.messagesPersistenceEnabled, shutdownTasks: []func() error{ database.Close, @@ -331,6 +350,12 @@ func NewMessenger( // Init analyzes chats and contacts in order to setup filters // which are responsible for retrieving messages. func (m *Messenger) Init() error { + m.mutex.Lock() + defer m.mutex.Unlock() + + // Seed the for color generation + rand.Seed(time.Now().Unix()) + logger := m.logger.With(zap.String("site", "Init")) var ( @@ -340,11 +365,12 @@ func (m *Messenger) Init() error { // Get chat IDs and public keys from the existing chats. // TODO: Get only active chats by the query. - chats, err := m.Chats() + chats, err := m.persistence.Chats() if err != nil { return err } for _, chat := range chats { + m.allChats[chat.ID] = chat if !chat.Active { continue } @@ -352,7 +378,11 @@ func (m *Messenger) Init() error { case ChatTypePublic: publicChatIDs = append(publicChatIDs, chat.ID) case ChatTypeOneToOne: - publicKeys = append(publicKeys, chat.PublicKey) + pk, err := chat.PublicKey() + if err != nil { + return err + } + publicKeys = append(publicKeys, pk) case ChatTypePrivateGroupChat: for _, member := range chat.Members { publicKey, err := member.PublicKey() @@ -367,11 +397,12 @@ func (m *Messenger) Init() error { } // Get chat IDs and public keys from the contacts. - contacts, err := m.Contacts() + contacts, err := m.persistence.Contacts() if err != nil { return err } for _, contact := range contacts { + m.allContacts[contact.ID] = contact // We only need filters for contacts added by us and not blocked. if !contact.IsAdded() || contact.IsBlocked() { continue @@ -462,7 +493,12 @@ func (m *Messenger) Mailservers() ([]string, error) { func (m *Messenger) Join(chat Chat) error { switch chat.ChatType { case ChatTypeOneToOne: - return m.transport.JoinPrivate(chat.PublicKey) + pk, err := chat.PublicKey() + if err != nil { + return err + } + + return m.transport.JoinPrivate(pk) case ChatTypePrivateGroupChat: members, err := chat.MembersAsPublicKeys() if err != nil { @@ -476,129 +512,399 @@ func (m *Messenger) Join(chat Chat) error { } } +// This is not accurate, it should not leave transport on removal of chat/group +// only once there is no more: Group chat with that member, one-to-one chat, contact added by us func (m *Messenger) Leave(chat Chat) error { - if chat.PublicKey != nil { - return m.transport.LeavePrivate(chat.PublicKey) - } else if chat.Name != "" { + switch chat.ChatType { + case ChatTypeOneToOne: + pk, err := chat.PublicKey() + if err != nil { + return err + } + return m.transport.LeavePrivate(pk) + case ChatTypePrivateGroupChat: + members, err := chat.MembersAsPublicKeys() + if err != nil { + return err + } + return m.transport.LeaveGroup(members) + case ChatTypePublic: return m.transport.LeavePublic(chat.Name) + default: + return errors.New("chat is neither public nor private") } - return errors.New("chat is neither public nor private") } -// TODO: consider moving to a ChatManager ??? -func (m *Messenger) CreateGroupChat(name string) (*Chat, error) { +func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers")) + logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members)) chat := createGroupChat() group, err := v1protocol.NewGroupWithCreator(name, m.identity) if err != nil { return nil, err } chat.updateChatFromProtocolGroup(group) - return &chat, nil -} -func (m *Messenger) AddMembersToChat(ctx context.Context, chat *Chat, members []*ecdsa.PublicKey) error { - group, err := newProtocolGroupFromChat(chat) - if err != nil { - return err - } - encodedMembers := make([]string, len(members)) - for idx, member := range members { - encodedMembers[idx] = types.EncodeHex(crypto.FromECDSAPub(member)) - } - event := v1protocol.NewMembersAddedEvent(encodedMembers, group.NextClockValue()) - err = group.ProcessEvent(&m.identity.PublicKey, event) - if err != nil { - return err - } - if err := m.propagateMembershipUpdates(ctx, group); err != nil { - return err - } - chat.updateChatFromProtocolGroup(group) - return m.SaveChat(*chat) -} - -func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chat *Chat) error { - group, err := newProtocolGroupFromChat(chat) - if err != nil { - return err - } - event := v1protocol.NewMemberJoinedEvent( - types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), - group.NextClockValue(), - ) - err = group.ProcessEvent(&m.identity.PublicKey, event) - if err != nil { - return err - } - if err := m.propagateMembershipUpdates(ctx, group); err != nil { - return err - } - chat.updateChatFromProtocolGroup(group) - return m.SaveChat(*chat) -} - -func (m *Messenger) propagateMembershipUpdates(ctx context.Context, group *v1protocol.Group) error { - events := make([]v1protocol.MembershipUpdateEvent, len(group.Updates())) - for idx, event := range group.Updates() { - events[idx] = event.MembershipUpdateEvent - } - update := v1protocol.MembershipUpdate{ - ChatID: group.ChatID(), - Events: events, - } - if err := update.Sign(m.identity); err != nil { - return err - } - recipients, err := stringSliceToPublicKeys(group.Members(), true) - if err != nil { - return err - } - // Filter out my key from the recipients - n := 0 - for _, recipient := range recipients { - if !isPubKeyEqual(recipient, &m.identity.PublicKey) { - recipients[n] = recipient - n++ - } - } - recipients = recipients[:n] - // Finally send membership updates to all recipients. - _, err = m.processor.SendMembershipUpdate( - ctx, - recipients, - group.ChatID(), - []v1protocol.MembershipUpdate{update}, - group.NextClockValue(), - ) - return err -} - -func (m *Messenger) SaveChat(chat Chat) error { - return m.persistence.SaveChat(chat) -} - -func (m *Messenger) Chats() ([]*Chat, error) { - return m.persistence.Chats() -} - -func (m *Messenger) DeleteChat(chatID string) error { - return m.persistence.DeleteChat(chatID) -} - -func (m *Messenger) chatByID(id string) (*Chat, error) { - chats, err := m.persistence.Chats() + // Add members + event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) if err != nil { return nil, err } - for _, c := range chats { - if c.ID == id { - return c, nil - } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err } - return nil, errChatNotFound + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{&chat} + response.Messages = buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations) + return &response, m.saveChat(&chat) } -func (m *Messenger) SaveContact(contact Contact) error { +func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string, member string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "RemoveMemberFromGroupChat")) + logger.Info("Removing member form group chat", zap.String("chatID", chatID), zap.String("member", member)) + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // We save the initial recipients as we want to send updates to also + // the members kicked out + oldRecipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + // Remove member + event := v1protocol.NewMemberRemovedEvent(member, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + if _, err := m.propagateMembershipUpdates(ctx, group, oldRecipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations) + return &response, m.saveChat(chat) +} + +func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "AddMembersFromGroupChat")) + logger.Info("Adding members form group chat", zap.String("chatID", chatID), zap.Any("members", members)) + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // Add members + event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + logger := m.logger.With(zap.String("site", "AddAdminsToGroupChat")) + logger.Info("Add admins to group chat", zap.String("chatID", chatID), zap.Any("members", members)) + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + + // Add members + event := v1protocol.NewAdminsAddedEvent(members, group.NextClockValue()) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + err := m.Join(*chat) + if err != nil { + return nil, err + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + event := v1protocol.NewMemberJoinedEvent( + group.NextClockValue(), + ) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + + return &response, m.saveChat(chat) +} + +func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + + var response MessengerResponse + + chat, ok := m.allChats[chatID] + if !ok { + return nil, errors.New("can't find chat") + } + + err := m.Leave(*chat) + if err != nil { + return nil, err + } + + group, err := newProtocolGroupFromChat(chat) + if err != nil { + return nil, err + } + event := v1protocol.NewMemberRemovedEvent( + types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + group.NextClockValue(), + ) + event.ChatID = chat.ID + err = event.Sign(m.identity) + if err != nil { + return nil, err + } + + err = group.ProcessEvent(event) + if err != nil { + return nil, err + } + + recipients, err := stringSliceToPublicKeys(group.Members(), true) + if err != nil { + return nil, err + } + + if _, err := m.propagateMembershipUpdates(ctx, group, recipients, nil); err != nil { + return nil, err + } + chat.updateChatFromProtocolGroup(group) + chat.Active = false + + response.Chats = []*Chat{chat} + response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) + return &response, m.saveChat(chat) +} + +func (m *Messenger) propagateMembershipUpdates(ctx context.Context, group *v1protocol.Group, recipients []*ecdsa.PublicKey, chatMessage *protobuf.ChatMessage) ([]byte, error) { + hasPairedDevices, err := m.hasPairedDevices() + if err != nil { + return nil, err + } + + if !hasPairedDevices { + // Filter out my key from the recipients + n := 0 + for _, recipient := range recipients { + if !isPubKeyEqual(recipient, &m.identity.PublicKey) { + recipients[n] = recipient + n++ + } + } + recipients = recipients[:n] + } + // Finally send membership updates to all recipients. + return m.processor.SendMembershipUpdate( + ctx, + recipients, + group, + chatMessage, + ) +} + +func (m *Messenger) saveChat(chat *Chat) error { + err := m.persistence.SaveChat(*chat) + if err != nil { + return err + } + m.allChats[chat.ID] = chat + + return nil +} + +func (m *Messenger) saveChats(chats []*Chat) error { + err := m.persistence.SaveChats(chats) + if err != nil { + return err + } + for _, chat := range chats { + m.allChats[chat.ID] = chat + } + + return nil + +} + +func (m *Messenger) SaveChat(chat *Chat) error { + m.mutex.Lock() + defer m.mutex.Unlock() + err := m.saveChat(chat) + if err != nil { + return err + } + + return nil +} + +func (m *Messenger) Chats() []*Chat { + m.mutex.Lock() + defer m.mutex.Unlock() + + var chats []*Chat + + for _, c := range m.allChats { + chats = append(chats, c) + } + + return chats +} + +func (m *Messenger) DeleteChat(chatID string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + err := m.persistence.DeleteChat(chatID) + if err != nil { + return err + } + delete(m.allChats, chatID) + + return nil +} + +func (m *Messenger) SaveContact(contact *Contact) error { + m.mutex.Lock() + defer m.mutex.Unlock() + identicon, err := identicon.GenerateBase64(contact.ID) if err != nil { return err @@ -613,15 +919,39 @@ func (m *Messenger) SaveContact(contact Contact) error { contact.Alias = name - return m.persistence.SaveContact(contact, nil) + err = m.persistence.SaveContact(contact, nil) + if err != nil { + return err + } + + m.allContacts[contact.ID] = contact + return nil } -func (m *Messenger) BlockContact(contact Contact) ([]*Chat, error) { - return m.persistence.BlockContact(contact) +func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + chats, err := m.persistence.BlockContact(contact) + if err != nil { + return nil, err + } + m.allContacts[contact.ID] = contact + for _, chat := range chats { + m.allChats[chat.ID] = chat + } + delete(m.allChats, contact.ID) + return chats, nil } -func (m *Messenger) Contacts() ([]*Contact, error) { - return m.persistence.Contacts() +func (m *Messenger) Contacts() []*Contact { + m.mutex.Lock() + defer m.mutex.Unlock() + var contacts []*Contact + for _, contact := range m.allContacts { + contacts = append(contacts, contact) + } + + return contacts } func timestampInMs() uint64 { @@ -630,6 +960,9 @@ func timestampInMs() uint64 { // ReSendChatMessage pulls a message from the database and sends it again func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + logger := m.logger.With(zap.String("site", "ReSendChatMessage")) var response MessengerResponse message, err := m.persistence.MessageByID(messageID) @@ -643,28 +976,33 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M return nil, errors.New("message payload not found, can't resend message") } - chat, err := m.chatByID(message.LocalChatID) - if err != nil { - return nil, err + chat, ok := m.allChats[message.LocalChatID] + if !ok { + return nil, errors.New("chat not found") } switch chat.ChatType { case ChatTypeOneToOne: - publicKey := crypto.FromECDSAPub(chat.PublicKey) - logger.Debug("re-sending private message", zap.Binary("publicKey", publicKey)) - id, err := m.processor.SendPrivateRaw(ctx, chat.PublicKey, message.RawPayload) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + logger.Debug("re-sending private message") + id, err := m.processor.SendPrivateRaw(ctx, publicKey, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, message.RawPayload) + err = m.sendToPairedDevices(ctx, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } case ChatTypePublic: logger.Debug("re-sending public message", zap.String("chatName", chat.Name)) - id, err := m.processor.SendPublicRaw(ctx, chat.ID, message.RawPayload) + id, err := m.processor.SendPublicRaw(ctx, chat.ID, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -683,14 +1021,16 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M n++ } } - id, err := m.processor.SendGroupRaw(ctx, recipients[:n], message.RawPayload) + id, err := m.processor.SendGroupRaw(ctx, recipients[:n], message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, message.RawPayload) + err = m.sendToPairedDevices(ctx, message.RawPayload, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -704,15 +1044,23 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) (*M return &response, nil } -// sendToPairedDevices will check if we have any paired devices and send to them if necessary -func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte) error { +func (m *Messenger) hasPairedDevices() (bool, error) { activeInstallations, err := m.encryptor.GetOurActiveInstallations(&m.identity.PublicKey) + if err != nil { + return false, err + } + return len(activeInstallations) > 1, nil +} + +// sendToPairedDevices will check if we have any paired devices and send to them if necessary +func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte, messageType protobuf.ApplicationMetadataMessage_Type) error { + hasPairedDevices, err := m.hasPairedDevices() if err != nil { return err } // We send a message to any paired device - if len(activeInstallations) > 1 { - _, err := m.processor.SendPrivateRaw(ctx, &m.identity.PublicKey, payload) + if hasPairedDevices { + _, err := m.processor.SendPrivateRaw(ctx, &m.identity.PublicKey, payload, messageType) if err != nil { return err } @@ -722,13 +1070,16 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, payload []byte) err // SendChatMessage takes a minimal message and sends it based on the corresponding chat func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() + logger := m.logger.With(zap.String("site", "Send"), zap.String("chatID", message.ChatId)) var response MessengerResponse // A valid added chat is required. - chat, err := m.chatByID(message.ChatId) - if err != nil { - return nil, err + chat, ok := m.allChats[message.ChatId] + if !ok { + return nil, errors.New("Chat not found") } clock := chat.LastClockValue @@ -764,8 +1115,11 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes switch chat.ChatType { case ChatTypeOneToOne: - publicKey := crypto.FromECDSAPub(chat.PublicKey) - logger.Debug("sending private message", zap.Binary("publicKey", publicKey)) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + logger.Debug("sending private message") message.MessageType = protobuf.ChatMessage_ONE_TO_ONE encodedMessage, err := proto.Marshal(message) if err != nil { @@ -773,13 +1127,15 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } message.RawPayload = encodedMessage - id, err := m.processor.SendPrivateRaw(ctx, chat.PublicKey, encodedMessage) + id, err := m.processor.SendPrivateRaw(ctx, publicKey, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } message.ID = "0x" + hex.EncodeToString(id) - err = m.sendToPairedDevices(ctx, encodedMessage) + err = m.sendToPairedDevices(ctx, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -793,7 +1149,8 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } message.RawPayload = encodedMessage - id, err := m.processor.SendPublicRaw(ctx, chat.ID, encodedMessage) + id, err := m.processor.SendPublicRaw(ctx, chat.ID, encodedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + if err != nil { return nil, err } @@ -801,37 +1158,24 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes case ChatTypePrivateGroupChat: logger.Debug("sending public message", zap.String("chatName", chat.Name)) message.MessageType = protobuf.ChatMessage_PRIVATE_GROUP - encodedMessage, err := proto.Marshal(message) - if err != nil { - return nil, err - } - message.RawPayload = encodedMessage - logger.Debug("sending group message", zap.String("chatName", chat.Name)) recipients, err := chat.MembersAsPublicKeys() if err != nil { return nil, err } - n := 0 - for _, item := range recipients { - if !isPubKeyEqual(item, &m.identity.PublicKey) { - recipients[n] = item - n++ - } - } - id, err := m.processor.SendGroupRaw(ctx, recipients[:n], encodedMessage) + group, err := newProtocolGroupFromChat(chat) if err != nil { return nil, err } + encodedMessage, err := m.propagateMembershipUpdates(ctx, group, recipients, &message.ChatMessage) + if err != nil { + return nil, err + } + + id := v1protocol.MessageID(&m.identity.PublicKey, encodedMessage) message.ID = "0x" + hex.EncodeToString(id) - - err = m.sendToPairedDevices(ctx, encodedMessage) - if err != nil { - return nil, err - } - default: return nil, errors.New("chat type not supported") } @@ -849,9 +1193,6 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes chat.LastClockValue = clock chat.LastMessage = jsonMessage chat.Timestamp = int64(timestamp) - if err := m.SaveChat(*chat); err != nil { - return nil, err - } err = m.persistence.SaveMessagesLegacy([]*Message{message}) if err != nil { @@ -860,16 +1201,20 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes response.Chats = []*Chat{chat} response.Messages = []*Message{message} - return &response, nil + return &response, m.saveChat(chat) } // SendRaw takes encoded data, encrypts it and sends through the wire. // DEPRECATED func (m *Messenger) SendRaw(ctx context.Context, chat Chat, data []byte) ([]byte, error) { - if chat.PublicKey != nil { - return m.processor.SendPrivateRaw(ctx, chat.PublicKey, data) + publicKey, err := chat.PublicKey() + if err != nil { + return nil, err + } + if publicKey != nil { + return m.processor.SendPrivateRaw(ctx, publicKey, data, protobuf.ApplicationMetadataMessage_UNKNOWN) } else if chat.Name != "" { - return m.processor.SendPublicRaw(ctx, chat.Name, data) + return m.processor.SendPublicRaw(ctx, chat.Name, data, protobuf.ApplicationMetadataMessage_UNKNOWN) } return nil, errors.New("chat is neither public nor private") } @@ -885,36 +1230,121 @@ func (m *Messenger) RetrieveAll() (*MessengerResponse, error) { return m.handleRetrievedMessages(chatWithMessages) } +type ReceivedMessageState struct { + // Message is the protobuf message received + Message protobuf.ChatMessage + // MessageID is the ID of the message + MessageID string + // WhisperTimestamp is the whisper timestamp of the message + WhisperTimestamp uint64 + // Contact is the contact associated with the author of the message + Contact *Contact + // PublicKey is the public key of the author of the message + PublicKey *ecdsa.PublicKey + // List of chats modified + ModifiedChats map[string]bool + PostProcessor *postProcessor +} + +func (m *Messenger) handleChatMessage(state *ReceivedMessageState) (*Message, error) { + logger := m.logger.With(zap.String("site", "handleChatMessage")) + if err := ValidateReceivedChatMessage(&state.Message); err != nil { + logger.Warn("failed to validate message", zap.Error(err)) + return nil, err + } + receivedMessage := &Message{ + ID: state.MessageID, + ChatMessage: state.Message, + From: state.Contact.ID, + Alias: state.Contact.Alias, + SigPubKey: state.PublicKey, + Identicon: state.Contact.Identicon, + WhisperTimestamp: state.WhisperTimestamp, + } + receivedMessage.PrepareContent() + chat, err := state.PostProcessor.matchMessage(receivedMessage, m.allChats) + if err != nil { + return nil, err + } + + // If deleted-at is greater, ignore message + if chat.DeletedAtClockValue >= receivedMessage.Clock { + return nil, nil + } + + // Set the LocalChatID for the message + receivedMessage.LocalChatID = chat.ID + + if c, ok := m.allChats[chat.ID]; ok { + chat = c + } + + // Set the LocalChatID for the message + receivedMessage.LocalChatID = chat.ID + + // Increase unviewed count + if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { + chat.UnviewedMessagesCount++ + } else { + // Our own message, mark as sent + receivedMessage.OutgoingStatus = OutgoingStatusSent + } + + // Update chat timestamp + chat.Timestamp = int64(timestampInMs()) + // Update last clock value + if chat.LastClockValue <= receivedMessage.Clock { + chat.LastClockValue = receivedMessage.Clock + encodedLastMessage, err := json.Marshal(receivedMessage) + if err != nil { + return nil, err + } + chat.LastMessage = encodedLastMessage + } + + // Set chat active + chat.Active = true + // Set in the modified maps chat + state.ModifiedChats[chat.ID] = true + m.allChats[chat.ID] = chat + + return receivedMessage, nil +} + +func (m *Messenger) messageExists(messageID string, existingMessagesMap map[string]bool) (bool, error) { + if _, ok := existingMessagesMap[messageID]; ok { + return true, nil + } + + existingMessagesMap[messageID] = true + + // Check against the database, this is probably a bit slow for + // each message, but for now might do, we'll make it faster later + existingMessage, err := m.persistence.MessageByID(messageID) + if err != nil && err != errRecordNotFound { + return false, err + } + if existingMessage != nil { + return true, nil + } + return false, nil +} + func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() response := &MessengerResponse{ Chats: []*Chat{}, Messages: []*Message{}, } - allChats, err := m.persistence.Chats() - if err != nil { - return nil, err - } postProcessor := newPostProcessor(m, postProcessorConfig{MatchChat: true}) logger := m.logger.With(zap.String("site", "RetrieveAll")) rawMessages := make(map[transport.Filter][]*v1protocol.StatusMessage) - // We should query this instead - contacts, err := m.Contacts() - if err != nil { - return nil, err - } - - blockedContacts := make(map[string]bool) - for _, c := range contacts { - if c.IsBlocked() { - blockedContacts[c.ID] = true - } - } - - allContactsMap := make(map[string]*Contact) - allChatsMap := make(map[string]*Chat) + modifiedChats := make(map[string]bool) + modifiedContacts := make(map[string]bool) existingMessagesMap := make(map[string]bool) for chat, messages := range chatWithMessages { @@ -927,35 +1357,25 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } for _, msg := range statusMessages { + publicKey := msg.SigPubKey() + // Check for messages from blocked users - senderID := "0x" + hex.EncodeToString(crypto.FromECDSAPub(msg.SigPubKey())) - if blockedContacts[senderID] { + senderID := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + if _, ok := m.allContacts[senderID]; ok && m.allContacts[senderID].IsBlocked() { continue } // Don't process duplicates - messageID := "0x" + hex.EncodeToString(msg.ID) - if _, ok := existingMessagesMap[messageID]; ok { + messageID := types.EncodeHex(msg.ID) + exists, err := m.messageExists(messageID, existingMessagesMap) + if err != nil { + logger.Warn("failed to check message exists", zap.Error(err)) + } + if exists { continue } - existingMessagesMap[messageID] = true - - // Check against the database, this is probably a bit slow for - // each message, but for now might do, we'll make it faster later - existingMessage, err := m.persistence.MessageByID(messageID) - if err != nil && err != errRecordNotFound { - return nil, err - } - if existingMessage != nil { - continue - } - - publicKey := msg.SigPubKey() - if publicKey == nil { - return nil, errors.New("public key can't be nil") - } var contact *Contact - if c, ok := allContactsMap[senderID]; ok { + if c, ok := m.allContacts[senderID]; ok { contact = c } else { c, err := buildContact(publicKey) @@ -964,88 +1384,118 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } contact = c - allContactsMap[senderID] = c - response.Contacts = append(response.Contacts, c) + m.allContacts[senderID] = c + modifiedContacts[contact.ID] = true + } + messageState := &ReceivedMessageState{ + MessageID: messageID, + WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000, + Contact: contact, + PublicKey: publicKey, + ModifiedChats: modifiedChats, + PostProcessor: postProcessor, } if msg.ParsedMessage != nil { - if textMessage, ok := msg.ParsedMessage.(protobuf.ChatMessage); ok { - receivedMessage := &Message{ - ID: messageID, - ChatMessage: textMessage, - From: contact.ID, - Alias: contact.Alias, - SigPubKey: publicKey, - Identicon: contact.Identicon, - WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000, - } - receivedMessage.PrepareContent() + switch msg.ParsedMessage.(type) { + case protobuf.MembershipUpdateMessage: - chat, err := postProcessor.matchMessage(receivedMessage, allChats) + rawMembershipUpdate := msg.ParsedMessage.(protobuf.MembershipUpdateMessage) + membershipUpdate, err := v1protocol.MembershipUpdateMessageFromProtobuf(&rawMembershipUpdate) if err != nil { - logger.Warn("failed to match message", zap.String("receivedChatID", receivedMessage.ChatId), zap.Error(err)) + logger.Warn("failed to process membership update", zap.Error(err)) + continue + + } + + chat, systemMessages, err := HandleMembershipUpdate(m.allChats[membershipUpdate.ChatID], membershipUpdate, types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), m.systemMessagesTranslations) + if err != nil { + logger.Warn("failed to process membership update", zap.Error(err)) continue } - // If deleted-at is greater, ignore message - if chat.DeletedAtClockValue >= receivedMessage.Clock { - continue - } - - // Set the LocalChatID for the message - receivedMessage.LocalChatID = chat.ID - - if c, ok := allChatsMap[chat.ID]; ok { - chat = c - } - - // Increase unviewed count - if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { - chat.UnviewedMessagesCount++ - } else { - // Our own message, mark as sent - receivedMessage.OutgoingStatus = OutgoingStatusSent - } - - // Update chat timestamp - chat.Timestamp = int64(timestampInMs()) - // Update last clock value - if chat.LastClockValue <= receivedMessage.Clock { - chat.LastClockValue = receivedMessage.Clock - encodedLastMessage, err := json.Marshal(receivedMessage) + for _, message := range systemMessages { + messageID := message.ID + exists, err := m.messageExists(messageID, existingMessagesMap) if err != nil { - return nil, err + logger.Warn("failed to check message exists", zap.Error(err)) } - chat.LastMessage = encodedLastMessage + if exists { + continue + } + response.Messages = append(response.Messages, message) } - // Set chat active - chat.Active = true + // Store in chats map as it might be a new one + m.allChats[chat.ID] = chat // Set in the map - allChatsMap[chat.ID] = chat + modifiedChats[chat.ID] = true + + if rawMembershipUpdate.Message != nil { + messageState.Message = *rawMembershipUpdate.Message + receivedMessage, err := m.handleChatMessage(messageState) + if err != nil { + logger.Warn("failed to process message", zap.Error(err)) + continue + } + // Add to response + if receivedMessage != nil { + response.Messages = append(response.Messages, receivedMessage) + } + + } + + case protobuf.ChatMessage: + messageState.Message = msg.ParsedMessage.(protobuf.ChatMessage) + receivedMessage, err := m.handleChatMessage(messageState) + if err != nil { + logger.Warn("failed to process message", zap.Error(err)) + continue + } // Add to response - response.Messages = append(response.Messages, receivedMessage) + if receivedMessage != nil { + response.Messages = append(response.Messages, receivedMessage) + } + default: + // RawMessage, not processed here, pass straight to the client + rawMessages[chat] = append(rawMessages[chat], msg) + } } else { - // RawMessage, not processed here, pass straight to the client rawMessages[chat] = append(rawMessages[chat], msg) } - } } } - err = m.persistence.SetContactsGeneratedData(response.Contacts, nil) - if err != nil { - return nil, err + for id, _ := range modifiedChats { + response.Chats = append(response.Chats, m.allChats[id]) } - for _, c := range allChatsMap { - response.Chats = append(response.Chats, c) + for id, _ := range modifiedContacts { + response.Contacts = append(response.Contacts, m.allContacts[id]) } - m.persistence.SaveChats(response.Chats) - m.SaveMessages(response.Messages) + var err error + if len(response.Chats) > 0 { + err = m.saveChats(response.Chats) + if err != nil { + return nil, err + } + } + if len(response.Messages) > 0 { + err = m.SaveMessages(response.Messages) + if err != nil { + return nil, err + } + } + + if len(response.Contacts) > 0 { + err = m.persistence.SaveContacts(response.Contacts) + if err != nil { + return nil, err + } + } for filter, messages := range rawMessages { response.RawMessages = append(response.RawMessages, &RawResponse{Filter: &filter, Messages: messages}) @@ -1102,47 +1552,6 @@ func (m *Messenger) SaveMessages(messages []*Message) error { return m.persistence.SaveMessagesLegacy(messages) } -// AddSystemMessages format an array of system-messages and saves them to the database -// It's needed until group chats are fully in status-go. -func (m *Messenger) AddSystemMessages(messages []*Message) ([]*Message, error) { - timestamp := timestampInMs() - - for _, message := range messages { - message.LocalChatID = message.ChatId - message.Timestamp = timestamp - message.WhisperTimestamp = timestamp - message.Seen = true - - identicon, err := identicon.GenerateBase64(message.From) - if err != nil { - return nil, err - } - - message.Identicon = identicon - - alias, err := alias.GenerateFromPublicKeyString(message.From) - if err != nil { - return nil, err - } - - message.ID = "0x" + hex.EncodeToString(crypto.Keccak256([]byte(message.Text+message.From+strconv.FormatUint(message.Clock, 10)))) - message.Alias = alias - message.ContentType = protobuf.ChatMessage_STATUS - message.MessageType = protobuf.ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP - err = message.PrepareContent() - if err != nil { - return nil, err - } - } - - err := m.SaveMessages(messages) - if err != nil { - return nil, err - } - - return messages, nil -} - // DEPRECATED: required by status-react. func (m *Messenger) DeleteMessage(id string) error { return m.persistence.DeleteMessage(id) @@ -1155,7 +1564,19 @@ func (m *Messenger) DeleteMessagesByChatID(id string) error { // DEPRECATED: required by status-react. func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) error { - return m.persistence.MarkMessagesSeen(chatID, ids) + m.mutex.Lock() + defer m.mutex.Unlock() + + err := m.persistence.MarkMessagesSeen(chatID, ids) + if err != nil { + return err + } + chat, err := m.persistence.Chat(chatID) + if err != nil { + return err + } + m.allChats[chatID] = chat + return nil } // DEPRECATED: required by status-react. @@ -1189,26 +1610,7 @@ func newPostProcessor(m *Messenger, config postProcessorConfig) *postProcessor { } } -func (p *postProcessor) matchMessages(messages []*Message) ([]*Message, error) { - chats, err := p.persistence.Chats() - if err != nil { - return nil, err - } - - result := make([]*Message, 0, len(messages)) - for _, message := range messages { - chat, err := p.matchMessage(message, chats) - if err != nil { - p.logger.Error("failed to match a chat to a message", zap.Error(err)) - continue - } - message.LocalChatID = chat.ID - result = append(result, message) - } - return result, nil -} - -func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, error) { +func (p *postProcessor) matchMessage(message *Message, chats map[string]*Chat) (*Chat, error) { if message.SigPubKey == nil { p.logger.Error("public key can't be empty") return nil, errors.New("received a message with empty public key") @@ -1219,7 +1621,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // For public messages, all outgoing and incoming messages have the same chatID // equal to a public chat name. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { return nil, errors.New("received a public message from non-existing chat") } @@ -1228,7 +1630,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // It's a private message coming from us so we rely on Message.ChatId // If chat does not exist, it should be created to support multidevice synchronization. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { if len(chatID) != PubKeyStringLength { return nil, errors.New("invalid pubkey length") @@ -1244,9 +1646,6 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er } newChat := CreateOneToOneChat(chatID[:8], pubKey) - if err := p.persistence.SaveChat(newChat); err != nil { - return nil, errors.Wrap(err, "failed to save newly created chat") - } chat = &newChat } return chat, nil @@ -1254,13 +1653,10 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // It's an incoming private message. ChatID is calculated from the signature. // If a chat does not exist, a new one is created and saved. chatID := types.EncodeHex(crypto.FromECDSAPub(message.SigPubKey)) - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { // TODO: this should be a three-word name used in the mobile client newChat := CreateOneToOneChat(chatID[:8], message.SigPubKey) - if err := p.persistence.SaveChat(newChat); err != nil { - return nil, errors.Wrap(err, "failed to save newly created chat") - } chat = &newChat } return chat, nil @@ -1268,7 +1664,7 @@ func (p *postProcessor) matchMessage(message *Message, chats []*Chat) (*Chat, er // In the case of a group message, ChatID is the same for all messages belonging to a group. // It needs to be verified if the signature public key belongs to the chat. chatID := message.ChatId - chat := findChatByID(chatID, chats) + chat := chats[chatID] if chat == nil { return nil, errors.New("received group chat message for non-existing chat") } @@ -1305,6 +1701,8 @@ func Identicon(id string) (string, error) { // VerifyENSNames verifies that a registered ENS name matches the expected public key func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetails []enstypes.ENSDetails) (map[string]enstypes.ENSResponse, error) { + m.mutex.Lock() + defer m.mutex.Unlock() verifier := m.node.NewENSVerifier(m.logger) ensResponse, err := verifier.CheckBatch(ensDetails, rpcEndpoint, contractAddress) @@ -1316,9 +1714,12 @@ func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetai var contacts []*Contact for _, details := range ensResponse { if details.Error == nil { - contact, err := buildContact(details.PublicKey) - if err != nil { - return nil, err + contact, ok := m.allContacts[details.PublicKeyString] + if !ok { + contact, err = buildContact(details.PublicKey) + if err != nil { + return nil, err + } } contact.ENSVerified = details.Verified contact.ENSVerifiedAt = details.VerifiedAt @@ -1335,7 +1736,7 @@ func (m *Messenger) VerifyENSNames(rpcEndpoint, contractAddress string, ensDetai } if len(contacts) != 0 { - err = m.persistence.SetContactsENSData(contacts) + err = m.persistence.SaveContacts(contacts) if err != nil { return nil, err } diff --git a/vendor/github.com/status-im/status-go/protocol/migrations/migrations.go b/vendor/github.com/status-im/status-go/protocol/migrations/migrations.go index e000dc621..4ff613eac 100644 --- a/vendor/github.com/status-im/status-go/protocol/migrations/migrations.go +++ b/vendor/github.com/status-im/status-go/protocol/migrations/migrations.go @@ -114,7 +114,7 @@ func _000001_initUpDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000001_init.up.db.sql", size: 832, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000001_init.up.db.sql", size: 832, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0xa4, 0xac, 0x0, 0xd3, 0x19, 0x53, 0x35, 0x91, 0x1c, 0x94, 0xea, 0xde, 0xa7, 0x75, 0xb6, 0x73, 0x1d, 0x42, 0x14, 0xca, 0x84, 0x5b, 0xdb, 0x10, 0x94, 0x28, 0xc0, 0x33, 0x95, 0x7f, 0xf}} return a, nil } @@ -154,7 +154,7 @@ func _000002_add_chatsUpDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 495, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 495, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6e, 0xca, 0x2b, 0xf7, 0xca, 0x21, 0xda, 0x17, 0x1f, 0x97, 0xa8, 0x12, 0xb5, 0x6c, 0xad, 0x92, 0xe7, 0x2, 0xaf, 0x1, 0xcb, 0x5e, 0xe9, 0x71, 0xc4, 0x81, 0xa7, 0x3, 0x93, 0x5b, 0x73, 0x73}} return a, nil } @@ -234,7 +234,7 @@ func _000004_user_messages_compatibilityUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000004_user_messages_compatibility.up.sql", size: 980, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "000004_user_messages_compatibility.up.sql", size: 980, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x7a, 0xba, 0xae, 0x6d, 0xef, 0x69, 0x12, 0x6b, 0x48, 0xe3, 0xa7, 0xad, 0x21, 0x4a, 0xcf, 0x4f, 0xbc, 0x14, 0xc1, 0x19, 0x69, 0x1c, 0xc, 0xa2, 0x3d, 0xbc, 0x12, 0x32, 0x71, 0x76, 0x15}} return a, nil } @@ -274,7 +274,7 @@ func _1567112142_user_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1567112142_user_messages.up.sql", size: 543, mode: os.FileMode(0644), modTime: time.Unix(1575009877, 0)} + info := bindataFileInfo{name: "1567112142_user_messages.up.sql", size: 543, mode: os.FileMode(0644), modTime: time.Unix(1575563165, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xff, 0xc0, 0x47, 0x32, 0xa9, 0xa4, 0x6, 0x63, 0x6b, 0xe7, 0x79, 0x2b, 0x80, 0x52, 0x2b, 0x6f, 0xf9, 0x9d, 0x9a, 0xc2, 0xa9, 0x7a, 0xf7, 0x4d, 0x14, 0x12, 0x21, 0x10, 0xc4, 0x30, 0x42, 0xaa}} return a, nil } diff --git a/vendor/github.com/status-im/status-go/protocol/persistence.go b/vendor/github.com/status-im/status-go/protocol/persistence.go index 730be6fb1..3100b3bb1 100644 --- a/vendor/github.com/status-im/status-go/protocol/persistence.go +++ b/vendor/github.com/status-im/status-go/protocol/persistence.go @@ -5,10 +5,8 @@ import ( "context" "database/sql" "encoding/gob" - "encoding/hex" "github.com/pkg/errors" - "github.com/status-im/status-go/eth-node/crypto" ) var ( @@ -45,6 +43,26 @@ func (db sqlitePersistence) SaveChats(chats []*Chat) error { return nil } +func (db sqlitePersistence) SaveContacts(contacts []*Contact) error { + tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) + defer func() { + if err == nil { + err = tx.Commit() + return + } + // don't shadow original error + _ = tx.Rollback() + }() + + for _, contact := range contacts { + err := db.SaveContact(contact, tx) + if err != nil { + return err + } + } + return nil +} + func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { var err error if tx == nil { @@ -62,21 +80,6 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { }() } - pkey := []byte{} - // For one to one chatID is an encoded public key - if chat.ChatType == ChatTypeOneToOne { - pkey, err = hex.DecodeString(chat.ID[2:]) - if err != nil { - return err - } - // Safety check, make sure is well formed - _, err := crypto.UnmarshalPubkey(pkey) - if err != nil { - return err - } - - } - // Encode members var encodedMembers bytes.Buffer memberEncoder := gob.NewEncoder(&encodedMembers) @@ -94,8 +97,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { } // Insert record - stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, public_key, unviewed_message_count, last_clock_value, last_message, members, membership_updates) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) + stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { return err } @@ -109,7 +112,6 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { chat.ChatType, chat.Timestamp, chat.DeletedAtClockValue, - pkey, chat.UnviewedMessagesCount, chat.LastClockValue, chat.LastMessage, @@ -157,7 +159,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { type, timestamp, deleted_at_clock_value, - public_key, unviewed_message_count, last_clock_value, last_message, @@ -176,7 +177,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { chat Chat encodedMembers []byte encodedMembershipUpdates []byte - pkey []byte ) err = rows.Scan( &chat.ID, @@ -186,7 +186,6 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { &chat.ChatType, &chat.Timestamp, &chat.DeletedAtClockValue, - &pkey, &chat.UnviewedMessagesCount, &chat.LastClockValue, &chat.LastMessage, @@ -211,18 +210,73 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { return } - if len(pkey) != 0 { - chat.PublicKey, err = crypto.UnmarshalPubkey(pkey) - if err != nil { - return - } - } chats = append(chats, &chat) } return } +func (db sqlitePersistence) Chat(chatID string) (*Chat, error) { + var ( + chat Chat + encodedMembers []byte + encodedMembershipUpdates []byte + ) + + err := db.db.QueryRow(` + SELECT + id, + name, + color, + active, + type, + timestamp, + deleted_at_clock_value, + unviewed_message_count, + last_clock_value, + last_message, + members, + membership_updates + FROM chats + WHERE id = ? + `, chatID).Scan(&chat.ID, + &chat.Name, + &chat.Color, + &chat.Active, + &chat.ChatType, + &chat.Timestamp, + &chat.DeletedAtClockValue, + &chat.UnviewedMessagesCount, + &chat.LastClockValue, + &chat.LastMessage, + &encodedMembers, + &encodedMembershipUpdates, + ) + switch err { + case sql.ErrNoRows: + return nil, nil + case nil: + // Restore members + membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers)) + err = membersDecoder.Decode(&chat.Members) + if err != nil { + return nil, err + } + + // Restore membership updates + membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates)) + err = membershipUpdatesDecoder.Decode(&chat.MembershipUpdates) + if err != nil { + return nil, err + } + + return &chat, nil + } + + return nil, err + +} + func (db sqlitePersistence) Contacts() ([]*Contact, error) { rows, err := db.db.Query(` SELECT @@ -293,83 +347,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { return response, nil } -func (db sqlitePersistence) SetContactsENSData(contacts []*Contact) error { - tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) - if err != nil { - return err - } - defer func() { - if err == nil { - err = tx.Commit() - return - } - // don't shadow original error - _ = tx.Rollback() - }() - - // Ensure contacts exists - - err = db.SetContactsGeneratedData(contacts, tx) - if err != nil { - return err - } - - // Update ens data - for _, contact := range contacts { - _, err := tx.Exec(`UPDATE contacts SET name = ?, ens_verified = ? , ens_verified_at = ? WHERE id = ?`, contact.Name, contact.ENSVerified, contact.ENSVerifiedAt, contact.ID) - if err != nil { - return err - } - } - - return nil -} - -// SetContactsGeneratedData sets a contact generated data if not existing already -// in the database -func (db sqlitePersistence) SetContactsGeneratedData(contacts []*Contact, tx *sql.Tx) (err error) { - if tx == nil { - tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) - if err != nil { - return err - } - defer func() { - if err == nil { - err = tx.Commit() - return - - } - // don't shadow original error - _ = tx.Rollback() - }() - } - - for _, contact := range contacts { - _, err = tx.Exec(` - INSERT OR IGNORE INTO contacts( - id, - address, - name, - alias, - identicon, - photo, - last_updated, - tribute_to_talk - ) VALUES (?, ?, "", ?, ?, "", 0, "")`, - contact.ID, - contact.Address, - contact.Alias, - contact.Identicon, - ) - if err != nil { - return - } - } - - return -} - -func (db sqlitePersistence) SaveContact(contact Contact, tx *sql.Tx) (err error) { +func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error) { if tx == nil { tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { diff --git a/vendor/github.com/status-im/status-go/protocol/persistence_legacy.go b/vendor/github.com/status-im/status-go/protocol/persistence_legacy.go index 362b27bb3..de530c95c 100644 --- a/vendor/github.com/status-im/status-go/protocol/persistence_legacy.go +++ b/vendor/github.com/status-im/status-go/protocol/persistence_legacy.go @@ -406,7 +406,7 @@ func (db sqlitePersistence) UpdateMessageOutgoingStatus(id string, newOutgoingSt } // BlockContact updates a contact, deletes all the messages and 1-to-1 chat, updates the unread messages count and returns a map with the new count -func (db sqlitePersistence) BlockContact(contact Contact) ([]*Chat, error) { +func (db sqlitePersistence) BlockContact(contact *Contact) ([]*Chat, error) { var chats []*Chat tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.pb.go b/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.pb.go new file mode 100644 index 000000000..ac37121f0 --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: application_metadata_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type ApplicationMetadataMessage_Type int32 + +const ( + ApplicationMetadataMessage_UNKNOWN ApplicationMetadataMessage_Type = 0 + ApplicationMetadataMessage_CHAT_MESSAGE ApplicationMetadataMessage_Type = 1 + ApplicationMetadataMessage_CONTACT_REQUEST ApplicationMetadataMessage_Type = 2 + ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE ApplicationMetadataMessage_Type = 3 + ApplicationMetadataMessage_PAIR_INSTALLATION ApplicationMetadataMessage_Type = 4 + ApplicationMetadataMessage_SYNC_INSTALLATION ApplicationMetadataMessage_Type = 5 +) + +var ApplicationMetadataMessage_Type_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CHAT_MESSAGE", + 2: "CONTACT_REQUEST", + 3: "MEMBERSHIP_UPDATE_MESSAGE", + 4: "PAIR_INSTALLATION", + 5: "SYNC_INSTALLATION", +} + +var ApplicationMetadataMessage_Type_value = map[string]int32{ + "UNKNOWN": 0, + "CHAT_MESSAGE": 1, + "CONTACT_REQUEST": 2, + "MEMBERSHIP_UPDATE_MESSAGE": 3, + "PAIR_INSTALLATION": 4, + "SYNC_INSTALLATION": 5, +} + +func (x ApplicationMetadataMessage_Type) String() string { + return proto.EnumName(ApplicationMetadataMessage_Type_name, int32(x)) +} + +func (ApplicationMetadataMessage_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ad09a6406fcf24c7, []int{0, 0} +} + +type ApplicationMetadataMessage struct { + // Signature of the payload field + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + // This is the encoded protobuf of the application level message, i.e ChatMessage + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + // The type of protobuf message sent + Type ApplicationMetadataMessage_Type `protobuf:"varint,3,opt,name=type,proto3,enum=protobuf.ApplicationMetadataMessage_Type" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ApplicationMetadataMessage) Reset() { *m = ApplicationMetadataMessage{} } +func (m *ApplicationMetadataMessage) String() string { return proto.CompactTextString(m) } +func (*ApplicationMetadataMessage) ProtoMessage() {} +func (*ApplicationMetadataMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_ad09a6406fcf24c7, []int{0} +} + +func (m *ApplicationMetadataMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ApplicationMetadataMessage.Unmarshal(m, b) +} +func (m *ApplicationMetadataMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ApplicationMetadataMessage.Marshal(b, m, deterministic) +} +func (m *ApplicationMetadataMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplicationMetadataMessage.Merge(m, src) +} +func (m *ApplicationMetadataMessage) XXX_Size() int { + return xxx_messageInfo_ApplicationMetadataMessage.Size(m) +} +func (m *ApplicationMetadataMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ApplicationMetadataMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_ApplicationMetadataMessage proto.InternalMessageInfo + +func (m *ApplicationMetadataMessage) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *ApplicationMetadataMessage) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *ApplicationMetadataMessage) GetType() ApplicationMetadataMessage_Type { + if m != nil { + return m.Type + } + return ApplicationMetadataMessage_UNKNOWN +} + +func init() { + proto.RegisterEnum("protobuf.ApplicationMetadataMessage_Type", ApplicationMetadataMessage_Type_name, ApplicationMetadataMessage_Type_value) + proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") +} + +func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) } + +var fileDescriptor_ad09a6406fcf24c7 = []byte{ + // 269 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x8e, 0x41, 0x4b, 0xc3, 0x30, + 0x18, 0x86, 0x6d, 0x57, 0x9d, 0x7e, 0x0e, 0xad, 0x11, 0xa1, 0x8a, 0xc2, 0xe8, 0x69, 0x5e, 0x7a, + 0xd0, 0xb3, 0x87, 0x58, 0x83, 0x2b, 0xae, 0x69, 0x4d, 0x52, 0xc4, 0x53, 0xc8, 0x5c, 0x1c, 0x85, + 0x6d, 0x0d, 0x6b, 0x76, 0xe8, 0x2f, 0xf0, 0x57, 0xf8, 0x5f, 0x65, 0xd5, 0x39, 0x3c, 0x78, 0x0a, + 0xef, 0x93, 0xf7, 0xe1, 0xfd, 0x20, 0x54, 0xc6, 0xcc, 0xca, 0x37, 0x65, 0xcb, 0x6a, 0x21, 0xe7, + 0xda, 0xaa, 0x89, 0xb2, 0x4a, 0xce, 0x75, 0x5d, 0xab, 0xa9, 0x8e, 0xcc, 0xb2, 0xb2, 0x15, 0xda, + 0x6f, 0x9f, 0xf1, 0xea, 0x3d, 0xfc, 0x74, 0xe1, 0x02, 0x6f, 0x85, 0xf4, 0xa7, 0x9f, 0x7e, 0xd7, + 0xd1, 0x25, 0x1c, 0xd4, 0xe5, 0x74, 0xa1, 0xec, 0x6a, 0xa9, 0x03, 0xa7, 0xef, 0x0c, 0x7a, 0x6c, + 0x0b, 0x50, 0x00, 0x5d, 0xa3, 0x9a, 0x59, 0xa5, 0x26, 0x81, 0xdb, 0xfe, 0x6d, 0x22, 0xba, 0x03, + 0xcf, 0x36, 0x46, 0x07, 0x9d, 0xbe, 0x33, 0x38, 0xba, 0xb9, 0x8e, 0x36, 0x7b, 0xd1, 0xff, 0x5b, + 0x91, 0x68, 0x8c, 0x66, 0xad, 0x16, 0x7e, 0x38, 0xe0, 0xad, 0x23, 0x3a, 0x84, 0x6e, 0x41, 0x9f, + 0x68, 0xf6, 0x42, 0xfd, 0x1d, 0xe4, 0x43, 0x2f, 0x1e, 0x62, 0x21, 0x53, 0xc2, 0x39, 0x7e, 0x24, + 0xbe, 0x83, 0x4e, 0xe1, 0x38, 0xce, 0xa8, 0xc0, 0xb1, 0x90, 0x8c, 0x3c, 0x17, 0x84, 0x0b, 0xdf, + 0x45, 0x57, 0x70, 0x9e, 0x92, 0xf4, 0x9e, 0x30, 0x3e, 0x4c, 0x72, 0x59, 0xe4, 0x0f, 0x58, 0x90, + 0x5f, 0xa7, 0x83, 0xce, 0xe0, 0x24, 0xc7, 0x09, 0x93, 0x09, 0xe5, 0x02, 0x8f, 0x46, 0x58, 0x24, + 0x19, 0xf5, 0xbd, 0x35, 0xe6, 0xaf, 0x34, 0xfe, 0x8b, 0x77, 0xc7, 0x7b, 0xed, 0xe5, 0xb7, 0x5f, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xb9, 0xdb, 0x0d, 0x56, 0x56, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.proto b/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.proto new file mode 100644 index 000000000..9d99259ca --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/application_metadata_message.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package protobuf; + +message ApplicationMetadataMessage { + // Signature of the payload field + bytes signature = 1; + // This is the encoded protobuf of the application level message, i.e ChatMessage + bytes payload = 2; + + // The type of protobuf message sent + Type type = 3; + + enum Type { + UNKNOWN = 0; + CHAT_MESSAGE = 1; + CONTACT_REQUEST = 2; + MEMBERSHIP_UPDATE_MESSAGE = 3; + PAIR_INSTALLATION = 4; + SYNC_INSTALLATION = 5; + } +} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.pb.go b/vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.pb.go new file mode 100644 index 000000000..25250309a --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.pb.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: chat_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type ChatMessage_MessageType int32 + +const ( + ChatMessage_UNKNOWN_MESSAGE_TYPE ChatMessage_MessageType = 0 + ChatMessage_ONE_TO_ONE ChatMessage_MessageType = 1 + ChatMessage_PUBLIC_GROUP ChatMessage_MessageType = 2 + ChatMessage_PRIVATE_GROUP ChatMessage_MessageType = 3 + // Only local + ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP ChatMessage_MessageType = 4 +) + +var ChatMessage_MessageType_name = map[int32]string{ + 0: "UNKNOWN_MESSAGE_TYPE", + 1: "ONE_TO_ONE", + 2: "PUBLIC_GROUP", + 3: "PRIVATE_GROUP", + 4: "SYSTEM_MESSAGE_PRIVATE_GROUP", +} + +var ChatMessage_MessageType_value = map[string]int32{ + "UNKNOWN_MESSAGE_TYPE": 0, + "ONE_TO_ONE": 1, + "PUBLIC_GROUP": 2, + "PRIVATE_GROUP": 3, + "SYSTEM_MESSAGE_PRIVATE_GROUP": 4, +} + +func (x ChatMessage_MessageType) String() string { + return proto.EnumName(ChatMessage_MessageType_name, int32(x)) +} + +func (ChatMessage_MessageType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1, 0} +} + +type ChatMessage_ContentType int32 + +const ( + ChatMessage_UNKNOWN_CONTENT_TYPE ChatMessage_ContentType = 0 + ChatMessage_TEXT_PLAIN ChatMessage_ContentType = 1 + ChatMessage_STICKER ChatMessage_ContentType = 2 + ChatMessage_STATUS ChatMessage_ContentType = 3 + ChatMessage_EMOJI ChatMessage_ContentType = 4 + ChatMessage_COMMAND ChatMessage_ContentType = 5 + ChatMessage_COMMAND_REQUEST ChatMessage_ContentType = 6 +) + +var ChatMessage_ContentType_name = map[int32]string{ + 0: "UNKNOWN_CONTENT_TYPE", + 1: "TEXT_PLAIN", + 2: "STICKER", + 3: "STATUS", + 4: "EMOJI", + 5: "COMMAND", + 6: "COMMAND_REQUEST", +} + +var ChatMessage_ContentType_value = map[string]int32{ + "UNKNOWN_CONTENT_TYPE": 0, + "TEXT_PLAIN": 1, + "STICKER": 2, + "STATUS": 3, + "EMOJI": 4, + "COMMAND": 5, + "COMMAND_REQUEST": 6, +} + +func (x ChatMessage_ContentType) String() string { + return proto.EnumName(ChatMessage_ContentType_name, int32(x)) +} + +func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1, 1} +} + +type StickerMessage struct { + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Pack int32 `protobuf:"varint,2,opt,name=pack,proto3" json:"pack,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StickerMessage) Reset() { *m = StickerMessage{} } +func (m *StickerMessage) String() string { return proto.CompactTextString(m) } +func (*StickerMessage) ProtoMessage() {} +func (*StickerMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{0} +} + +func (m *StickerMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StickerMessage.Unmarshal(m, b) +} +func (m *StickerMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StickerMessage.Marshal(b, m, deterministic) +} +func (m *StickerMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_StickerMessage.Merge(m, src) +} +func (m *StickerMessage) XXX_Size() int { + return xxx_messageInfo_StickerMessage.Size(m) +} +func (m *StickerMessage) XXX_DiscardUnknown() { + xxx_messageInfo_StickerMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_StickerMessage proto.InternalMessageInfo + +func (m *StickerMessage) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +func (m *StickerMessage) GetPack() int32 { + if m != nil { + return m.Pack + } + return 0 +} + +type ChatMessage struct { + // Lamport timestamp of the chat message + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + // Unix timestamps in milliseconds, currently not used as we use whisper as more reliable, but here + // so that we don't rely on it + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Text of the message + Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"` + // Id of the message that we are replying to + ResponseTo string `protobuf:"bytes,4,opt,name=response_to,json=responseTo,proto3" json:"response_to,omitempty"` + // Ens name of the sender + EnsName string `protobuf:"bytes,5,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` + // Chat id, this field is symmetric for public-chats and private group chats, + // but asymmetric in case of one-to-ones, as the sender will use the chat-id + // of the received, while the receiver will use the chat-id of the sender. + // Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order + ChatId string `protobuf:"bytes,6,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // The type of message (public/one-to-one/private-group-chat) + MessageType ChatMessage_MessageType `protobuf:"varint,7,opt,name=message_type,json=messageType,proto3,enum=protobuf.ChatMessage_MessageType" json:"message_type,omitempty"` + // The type of the content of the message + ContentType ChatMessage_ContentType `protobuf:"varint,8,opt,name=content_type,json=contentType,proto3,enum=protobuf.ChatMessage_ContentType" json:"content_type,omitempty"` + // Types that are valid to be assigned to Payload: + // *ChatMessage_Sticker + Payload isChatMessage_Payload `protobuf_oneof:"payload"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatMessage) Reset() { *m = ChatMessage{} } +func (m *ChatMessage) String() string { return proto.CompactTextString(m) } +func (*ChatMessage) ProtoMessage() {} +func (*ChatMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_263952f55fd35689, []int{1} +} + +func (m *ChatMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatMessage.Unmarshal(m, b) +} +func (m *ChatMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatMessage.Marshal(b, m, deterministic) +} +func (m *ChatMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatMessage.Merge(m, src) +} +func (m *ChatMessage) XXX_Size() int { + return xxx_messageInfo_ChatMessage.Size(m) +} +func (m *ChatMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ChatMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatMessage proto.InternalMessageInfo + +func (m *ChatMessage) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + +func (m *ChatMessage) GetTimestamp() uint64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *ChatMessage) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +func (m *ChatMessage) GetResponseTo() string { + if m != nil { + return m.ResponseTo + } + return "" +} + +func (m *ChatMessage) GetEnsName() string { + if m != nil { + return m.EnsName + } + return "" +} + +func (m *ChatMessage) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *ChatMessage) GetMessageType() ChatMessage_MessageType { + if m != nil { + return m.MessageType + } + return ChatMessage_UNKNOWN_MESSAGE_TYPE +} + +func (m *ChatMessage) GetContentType() ChatMessage_ContentType { + if m != nil { + return m.ContentType + } + return ChatMessage_UNKNOWN_CONTENT_TYPE +} + +type isChatMessage_Payload interface { + isChatMessage_Payload() +} + +type ChatMessage_Sticker struct { + Sticker *StickerMessage `protobuf:"bytes,9,opt,name=sticker,proto3,oneof"` +} + +func (*ChatMessage_Sticker) isChatMessage_Payload() {} + +func (m *ChatMessage) GetPayload() isChatMessage_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (m *ChatMessage) GetSticker() *StickerMessage { + if x, ok := m.GetPayload().(*ChatMessage_Sticker); ok { + return x.Sticker + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ChatMessage) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ChatMessage_Sticker)(nil), + } +} + +func init() { + proto.RegisterEnum("protobuf.ChatMessage_MessageType", ChatMessage_MessageType_name, ChatMessage_MessageType_value) + proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value) + proto.RegisterType((*StickerMessage)(nil), "protobuf.StickerMessage") + proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage") +} + +func init() { proto.RegisterFile("chat_message.proto", fileDescriptor_263952f55fd35689) } + +var fileDescriptor_263952f55fd35689 = []byte{ + // 462 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0xcf, 0x6f, 0xd3, 0x30, + 0x14, 0x5e, 0xda, 0xb4, 0x69, 0x5f, 0x46, 0x31, 0x66, 0x12, 0x46, 0x9a, 0x44, 0xe9, 0xa9, 0xa7, + 0x1e, 0x06, 0x07, 0xae, 0x5d, 0x66, 0x8d, 0xb0, 0xc5, 0x29, 0x8e, 0x0b, 0xec, 0x64, 0x79, 0xa9, + 0xa1, 0x55, 0x97, 0x1f, 0x6a, 0x8c, 0x44, 0x0f, 0xf0, 0x67, 0x73, 0x46, 0x71, 0x5a, 0xda, 0x5d, + 0x76, 0xf2, 0xfb, 0xbe, 0xf7, 0xbd, 0xef, 0xe5, 0x7d, 0x01, 0x9c, 0x2e, 0x95, 0x91, 0x99, 0xae, + 0x2a, 0xf5, 0x43, 0x4f, 0xca, 0x4d, 0x61, 0x0a, 0xdc, 0xb3, 0xcf, 0xfd, 0xcf, 0xef, 0xa3, 0x0f, + 0x30, 0x48, 0xcc, 0x2a, 0x5d, 0xeb, 0x4d, 0xd4, 0x28, 0x30, 0x06, 0x77, 0xa9, 0xaa, 0x25, 0x71, + 0x86, 0xce, 0xb8, 0xcf, 0x6d, 0x5d, 0x73, 0xa5, 0x4a, 0xd7, 0xa4, 0x35, 0x74, 0xc6, 0x1d, 0x6e, + 0xeb, 0xd1, 0x5f, 0x17, 0xfc, 0x60, 0xa9, 0xcc, 0x7e, 0xee, 0x0c, 0x3a, 0xe9, 0x43, 0x91, 0xae, + 0xed, 0xa0, 0xcb, 0x1b, 0x80, 0xcf, 0xa1, 0x6f, 0x56, 0x99, 0xae, 0x8c, 0xca, 0x4a, 0x3b, 0xee, + 0xf2, 0x03, 0x51, 0xfb, 0x1a, 0xfd, 0xcb, 0x90, 0x76, 0xb3, 0xab, 0xae, 0xf1, 0x1b, 0xf0, 0x37, + 0xba, 0x2a, 0x8b, 0xbc, 0xd2, 0xd2, 0x14, 0xc4, 0xb5, 0x2d, 0xd8, 0x53, 0xa2, 0xc0, 0xaf, 0xa1, + 0xa7, 0xf3, 0x4a, 0xe6, 0x2a, 0xd3, 0xa4, 0x63, 0xbb, 0x9e, 0xce, 0x2b, 0xa6, 0x32, 0x8d, 0x5f, + 0x81, 0x67, 0xaf, 0x5d, 0x2d, 0x48, 0xd7, 0x76, 0xba, 0x35, 0x0c, 0x17, 0xf8, 0x0a, 0x4e, 0x77, + 0x09, 0x48, 0xb3, 0x2d, 0x35, 0xf1, 0x86, 0xce, 0x78, 0x70, 0xf1, 0x76, 0xb2, 0xcf, 0x61, 0x72, + 0x74, 0xc9, 0x64, 0xf7, 0x8a, 0x6d, 0xa9, 0xb9, 0x9f, 0x1d, 0x40, 0xed, 0x92, 0x16, 0xb9, 0xd1, + 0xb9, 0x69, 0x5c, 0x7a, 0x4f, 0xb9, 0x04, 0x8d, 0xb2, 0x71, 0x49, 0x0f, 0x00, 0xbf, 0x07, 0xaf, + 0x6a, 0x22, 0x27, 0xfd, 0xa1, 0x33, 0xf6, 0x2f, 0xc8, 0xc1, 0xe0, 0xf1, 0xbf, 0xf8, 0x78, 0xc2, + 0xf7, 0xd2, 0xd1, 0x1f, 0xf0, 0x8f, 0xbe, 0x0b, 0x13, 0x38, 0x9b, 0xb3, 0x1b, 0x16, 0x7f, 0x65, + 0x32, 0xa2, 0x49, 0x32, 0xbd, 0xa6, 0x52, 0xdc, 0xcd, 0x28, 0x3a, 0xc1, 0x03, 0x80, 0x98, 0x51, + 0x29, 0x62, 0x19, 0x33, 0x8a, 0x1c, 0x8c, 0xe0, 0x74, 0x36, 0xbf, 0xbc, 0x0d, 0x03, 0x79, 0xcd, + 0xe3, 0xf9, 0x0c, 0xb5, 0xf0, 0x0b, 0x78, 0x36, 0xe3, 0xe1, 0x97, 0xa9, 0xa0, 0x3b, 0xaa, 0x8d, + 0x87, 0x70, 0x9e, 0xdc, 0x25, 0x82, 0x46, 0xff, 0xdd, 0x1e, 0x2b, 0xdc, 0xd1, 0x6f, 0xf0, 0x8f, + 0x2e, 0x3a, 0xde, 0x1f, 0xc4, 0x4c, 0x50, 0x26, 0x8e, 0xf6, 0x0b, 0xfa, 0x4d, 0xc8, 0xd9, 0xed, + 0x34, 0x64, 0xc8, 0xc1, 0x3e, 0x78, 0x89, 0x08, 0x83, 0x1b, 0xca, 0x51, 0x0b, 0x03, 0x74, 0x13, + 0x31, 0x15, 0xf3, 0x04, 0xb5, 0x71, 0x1f, 0x3a, 0x34, 0x8a, 0x3f, 0x85, 0xc8, 0xad, 0x35, 0x41, + 0x1c, 0x45, 0x53, 0x76, 0x85, 0x3a, 0xf8, 0x25, 0x3c, 0xdf, 0x01, 0xc9, 0xe9, 0xe7, 0x39, 0x4d, + 0x04, 0xea, 0x5e, 0xf6, 0xc1, 0x2b, 0xd5, 0xf6, 0xa1, 0x50, 0x8b, 0xfb, 0xae, 0x4d, 0xeb, 0xdd, + 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x19, 0x70, 0x9c, 0xd9, 0x02, 0x00, 0x00, +} diff --git a/protocol/protobuf/message.proto b/vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.proto similarity index 64% rename from protocol/protobuf/message.proto rename to vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.proto index ec773b99c..e7d2309da 100644 --- a/protocol/protobuf/message.proto +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/chat_message.proto @@ -35,36 +35,20 @@ message ChatMessage { } enum MessageType { - ONE_TO_ONE = 0; - PUBLIC_GROUP = 1; - PRIVATE_GROUP = 2; + UNKNOWN_MESSAGE_TYPE = 0; + ONE_TO_ONE = 1; + PUBLIC_GROUP = 2; + PRIVATE_GROUP = 3; // Only local - SYSTEM_MESSAGE_PRIVATE_GROUP = 3; + SYSTEM_MESSAGE_PRIVATE_GROUP = 4; } enum ContentType { - TEXT_PLAIN = 0; - STICKER = 1; - STATUS = 2; - EMOJI = 3; - COMMAND = 4; - COMMAND_REQUEST = 5; - } -} - -message ApplicationMetadataMessage { - // Signature of the payload field - bytes signature = 1; - // This is the encoded protobuf of the application level message, i.e ChatMessage - bytes payload = 2; - - // The type of protobuf message sent - MessageType message_type = 3; - - enum MessageType { - TEXT_MESSAGE = 0; - CONTACT_REQUEST = 1; - MEMBERSHIP_UPDATE = 2; - PAIR_INSTALLATION = 3; - SYNC_INSTALLATION = 4; + UNKNOWN_CONTENT_TYPE = 0; + TEXT_PLAIN = 1; + STICKER = 2; + STATUS = 3; + EMOJI = 4; + COMMAND = 5; + COMMAND_REQUEST = 6; } } diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.pb.go b/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.pb.go new file mode 100644 index 000000000..30dd3d1f2 --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.pb.go @@ -0,0 +1,227 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: membership_update_message.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type MembershipUpdateEvent_EventType int32 + +const ( + MembershipUpdateEvent_UNKNOWN MembershipUpdateEvent_EventType = 0 + MembershipUpdateEvent_CHAT_CREATED MembershipUpdateEvent_EventType = 1 + MembershipUpdateEvent_NAME_CHANGED MembershipUpdateEvent_EventType = 2 + MembershipUpdateEvent_MEMBERS_ADDED MembershipUpdateEvent_EventType = 3 + MembershipUpdateEvent_MEMBER_JOINED MembershipUpdateEvent_EventType = 4 + MembershipUpdateEvent_MEMBER_REMOVED MembershipUpdateEvent_EventType = 5 + MembershipUpdateEvent_ADMINS_ADDED MembershipUpdateEvent_EventType = 6 + MembershipUpdateEvent_ADMIN_REMOVED MembershipUpdateEvent_EventType = 7 +) + +var MembershipUpdateEvent_EventType_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CHAT_CREATED", + 2: "NAME_CHANGED", + 3: "MEMBERS_ADDED", + 4: "MEMBER_JOINED", + 5: "MEMBER_REMOVED", + 6: "ADMINS_ADDED", + 7: "ADMIN_REMOVED", +} + +var MembershipUpdateEvent_EventType_value = map[string]int32{ + "UNKNOWN": 0, + "CHAT_CREATED": 1, + "NAME_CHANGED": 2, + "MEMBERS_ADDED": 3, + "MEMBER_JOINED": 4, + "MEMBER_REMOVED": 5, + "ADMINS_ADDED": 6, + "ADMIN_REMOVED": 7, +} + +func (x MembershipUpdateEvent_EventType) String() string { + return proto.EnumName(MembershipUpdateEvent_EventType_name, int32(x)) +} + +func (MembershipUpdateEvent_EventType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{0, 0} +} + +type MembershipUpdateEvent struct { + // Lamport timestamp of the event + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + // List of public keys of objects of the action + Members []string `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"` + // Name of the chat for the CHAT_CREATED/NAME_CHANGED event types + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + // The type of the event + Type MembershipUpdateEvent_EventType `protobuf:"varint,4,opt,name=type,proto3,enum=protobuf.MembershipUpdateEvent_EventType" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MembershipUpdateEvent) Reset() { *m = MembershipUpdateEvent{} } +func (m *MembershipUpdateEvent) String() string { return proto.CompactTextString(m) } +func (*MembershipUpdateEvent) ProtoMessage() {} +func (*MembershipUpdateEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{0} +} + +func (m *MembershipUpdateEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MembershipUpdateEvent.Unmarshal(m, b) +} +func (m *MembershipUpdateEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MembershipUpdateEvent.Marshal(b, m, deterministic) +} +func (m *MembershipUpdateEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_MembershipUpdateEvent.Merge(m, src) +} +func (m *MembershipUpdateEvent) XXX_Size() int { + return xxx_messageInfo_MembershipUpdateEvent.Size(m) +} +func (m *MembershipUpdateEvent) XXX_DiscardUnknown() { + xxx_messageInfo_MembershipUpdateEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_MembershipUpdateEvent proto.InternalMessageInfo + +func (m *MembershipUpdateEvent) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + +func (m *MembershipUpdateEvent) GetMembers() []string { + if m != nil { + return m.Members + } + return nil +} + +func (m *MembershipUpdateEvent) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *MembershipUpdateEvent) GetType() MembershipUpdateEvent_EventType { + if m != nil { + return m.Type + } + return MembershipUpdateEvent_UNKNOWN +} + +// MembershipUpdateMessage is a message used to propagate information +// about group membership changes. +// For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. +type MembershipUpdateMessage struct { + // The chat id of the private group chat + ChatId string `protobuf:"bytes,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // A list of events for this group chat, first x bytes are the signature, then is a + // protobuf encoded MembershipUpdateEvent + Events [][]byte `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` + // An optional chat message + Message *ChatMessage `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MembershipUpdateMessage) Reset() { *m = MembershipUpdateMessage{} } +func (m *MembershipUpdateMessage) String() string { return proto.CompactTextString(m) } +func (*MembershipUpdateMessage) ProtoMessage() {} +func (*MembershipUpdateMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_8d37dd0dc857a6be, []int{1} +} + +func (m *MembershipUpdateMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MembershipUpdateMessage.Unmarshal(m, b) +} +func (m *MembershipUpdateMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MembershipUpdateMessage.Marshal(b, m, deterministic) +} +func (m *MembershipUpdateMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_MembershipUpdateMessage.Merge(m, src) +} +func (m *MembershipUpdateMessage) XXX_Size() int { + return xxx_messageInfo_MembershipUpdateMessage.Size(m) +} +func (m *MembershipUpdateMessage) XXX_DiscardUnknown() { + xxx_messageInfo_MembershipUpdateMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_MembershipUpdateMessage proto.InternalMessageInfo + +func (m *MembershipUpdateMessage) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *MembershipUpdateMessage) GetEvents() [][]byte { + if m != nil { + return m.Events + } + return nil +} + +func (m *MembershipUpdateMessage) GetMessage() *ChatMessage { + if m != nil { + return m.Message + } + return nil +} + +func init() { + proto.RegisterEnum("protobuf.MembershipUpdateEvent_EventType", MembershipUpdateEvent_EventType_name, MembershipUpdateEvent_EventType_value) + proto.RegisterType((*MembershipUpdateEvent)(nil), "protobuf.MembershipUpdateEvent") + proto.RegisterType((*MembershipUpdateMessage)(nil), "protobuf.MembershipUpdateMessage") +} + +func init() { proto.RegisterFile("membership_update_message.proto", fileDescriptor_8d37dd0dc857a6be) } + +var fileDescriptor_8d37dd0dc857a6be = []byte{ + // 340 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x41, 0x4f, 0xf2, 0x40, + 0x10, 0xfd, 0x0a, 0xa5, 0xfd, 0x3a, 0x20, 0xa9, 0x13, 0x91, 0xc6, 0x8b, 0x0d, 0xa7, 0x7a, 0xa9, + 0x09, 0x9e, 0x3d, 0xd4, 0xee, 0x46, 0xd0, 0xec, 0x92, 0xac, 0xa0, 0xc7, 0xa6, 0xc0, 0x2a, 0x44, + 0x0b, 0x0d, 0x14, 0x13, 0xe2, 0x4f, 0xf1, 0x8f, 0xf8, 0xf3, 0x4c, 0x97, 0x16, 0xa2, 0xf1, 0xb2, + 0xbb, 0xef, 0xcd, 0xbc, 0x37, 0x3b, 0x0f, 0xce, 0x13, 0x99, 0x8c, 0xe5, 0x6a, 0x3d, 0x9b, 0xa7, + 0xd1, 0x26, 0x9d, 0xc6, 0x99, 0x8c, 0x12, 0xb9, 0x5e, 0xc7, 0x2f, 0xd2, 0x4f, 0x57, 0xcb, 0x6c, + 0x89, 0xff, 0xd5, 0x35, 0xde, 0x3c, 0x9f, 0xe1, 0x64, 0x16, 0x67, 0x3f, 0xab, 0x9d, 0xaf, 0x0a, + 0xb4, 0xd8, 0xde, 0x61, 0xa4, 0x0c, 0xe8, 0xbb, 0x5c, 0x64, 0x78, 0x02, 0xb5, 0xc9, 0xdb, 0x72, + 0xf2, 0xea, 0x68, 0xae, 0xe6, 0xe9, 0x62, 0x07, 0xd0, 0x01, 0xb3, 0x18, 0xe8, 0x54, 0xdc, 0xaa, + 0x67, 0x89, 0x12, 0x22, 0x82, 0xbe, 0x88, 0x13, 0xe9, 0x54, 0x5d, 0xcd, 0xb3, 0x84, 0x7a, 0xe3, + 0x35, 0xe8, 0xd9, 0x36, 0x95, 0x8e, 0xee, 0x6a, 0x5e, 0xb3, 0x7b, 0xe1, 0x97, 0x5f, 0xf1, 0xff, + 0x1c, 0xe9, 0xab, 0x73, 0xb8, 0x4d, 0xa5, 0x50, 0xb2, 0xce, 0xa7, 0x06, 0xd6, 0x9e, 0xc3, 0x3a, + 0x98, 0x23, 0x7e, 0xcf, 0x07, 0x4f, 0xdc, 0xfe, 0x87, 0x36, 0x34, 0xc2, 0x5e, 0x30, 0x8c, 0x42, + 0x41, 0x83, 0x21, 0x25, 0xb6, 0x96, 0x33, 0x3c, 0x60, 0x34, 0x0a, 0x7b, 0x01, 0xbf, 0xa5, 0xc4, + 0xae, 0xe0, 0x31, 0x1c, 0x31, 0xca, 0x6e, 0xa8, 0x78, 0x88, 0x02, 0x42, 0x28, 0xb1, 0xab, 0x07, + 0x2a, 0xba, 0x1b, 0xf4, 0x39, 0x25, 0xb6, 0x8e, 0x08, 0xcd, 0x82, 0x12, 0x94, 0x0d, 0x1e, 0x29, + 0xb1, 0x6b, 0xb9, 0x57, 0x40, 0x58, 0x9f, 0x97, 0x42, 0x23, 0x17, 0x2a, 0x66, 0xdf, 0x64, 0x76, + 0x3e, 0xa0, 0xfd, 0x7b, 0x0d, 0xb6, 0xcb, 0x16, 0xdb, 0x60, 0xaa, 0xac, 0xe7, 0x53, 0x95, 0x9e, + 0x25, 0x8c, 0x1c, 0xf6, 0xa7, 0x78, 0x0a, 0x86, 0xcc, 0x17, 0xda, 0xa5, 0xd7, 0x10, 0x05, 0xc2, + 0xcb, 0x3c, 0x56, 0xa5, 0x55, 0xf9, 0xd5, 0xbb, 0xad, 0x43, 0x56, 0xe1, 0x2c, 0xce, 0x0a, 0x63, + 0x51, 0x76, 0x8d, 0x0d, 0x55, 0xbe, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x7f, 0xb8, 0xe8, 0x9e, + 0xff, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.proto b/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.proto new file mode 100644 index 000000000..8831b3f3c --- /dev/null +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/membership_update_message.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package protobuf; + +import "chat_message.proto"; + +message MembershipUpdateEvent { + // Lamport timestamp of the event + uint64 clock = 1; + // List of public keys of objects of the action + repeated string members = 2; + // Name of the chat for the CHAT_CREATED/NAME_CHANGED event types + string name = 3; + // The type of the event + EventType type = 4; + + enum EventType { + UNKNOWN = 0; + CHAT_CREATED = 1; + NAME_CHANGED = 2; + MEMBERS_ADDED = 3; + MEMBER_JOINED = 4; + MEMBER_REMOVED = 5; + ADMINS_ADDED = 6; + ADMIN_REMOVED = 7; + } +} + +// MembershipUpdateMessage is a message used to propagate information +// about group membership changes. +// For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. +message MembershipUpdateMessage { + // The chat id of the private group chat + string chat_id = 1; + // A list of events for this group chat, first x bytes are the signature, then is a + // protobuf encoded MembershipUpdateEvent + repeated bytes events = 2; + // An optional chat message + ChatMessage message = 3; +} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/message.pb.go b/vendor/github.com/status-im/status-go/protocol/protobuf/message.pb.go deleted file mode 100644 index f3313c134..000000000 --- a/vendor/github.com/status-im/status-go/protocol/protobuf/message.pb.go +++ /dev/null @@ -1,421 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: message.proto - -package protobuf - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ChatMessage_MessageType int32 - -const ( - ChatMessage_ONE_TO_ONE ChatMessage_MessageType = 0 - ChatMessage_PUBLIC_GROUP ChatMessage_MessageType = 1 - ChatMessage_PRIVATE_GROUP ChatMessage_MessageType = 2 - // Only local - ChatMessage_SYSTEM_MESSAGE_PRIVATE_GROUP ChatMessage_MessageType = 3 -) - -var ChatMessage_MessageType_name = map[int32]string{ - 0: "ONE_TO_ONE", - 1: "PUBLIC_GROUP", - 2: "PRIVATE_GROUP", - 3: "SYSTEM_MESSAGE_PRIVATE_GROUP", -} - -var ChatMessage_MessageType_value = map[string]int32{ - "ONE_TO_ONE": 0, - "PUBLIC_GROUP": 1, - "PRIVATE_GROUP": 2, - "SYSTEM_MESSAGE_PRIVATE_GROUP": 3, -} - -func (x ChatMessage_MessageType) String() string { - return proto.EnumName(ChatMessage_MessageType_name, int32(x)) -} - -func (ChatMessage_MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1, 0} -} - -type ChatMessage_ContentType int32 - -const ( - ChatMessage_TEXT_PLAIN ChatMessage_ContentType = 0 - ChatMessage_STICKER ChatMessage_ContentType = 1 - ChatMessage_STATUS ChatMessage_ContentType = 2 - ChatMessage_EMOJI ChatMessage_ContentType = 3 - ChatMessage_COMMAND ChatMessage_ContentType = 4 - ChatMessage_COMMAND_REQUEST ChatMessage_ContentType = 5 -) - -var ChatMessage_ContentType_name = map[int32]string{ - 0: "TEXT_PLAIN", - 1: "STICKER", - 2: "STATUS", - 3: "EMOJI", - 4: "COMMAND", - 5: "COMMAND_REQUEST", -} - -var ChatMessage_ContentType_value = map[string]int32{ - "TEXT_PLAIN": 0, - "STICKER": 1, - "STATUS": 2, - "EMOJI": 3, - "COMMAND": 4, - "COMMAND_REQUEST": 5, -} - -func (x ChatMessage_ContentType) String() string { - return proto.EnumName(ChatMessage_ContentType_name, int32(x)) -} - -func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1, 1} -} - -type ApplicationMetadataMessage_MessageType int32 - -const ( - ApplicationMetadataMessage_TEXT_MESSAGE ApplicationMetadataMessage_MessageType = 0 - ApplicationMetadataMessage_CONTACT_REQUEST ApplicationMetadataMessage_MessageType = 1 - ApplicationMetadataMessage_MEMBERSHIP_UPDATE ApplicationMetadataMessage_MessageType = 2 - ApplicationMetadataMessage_PAIR_INSTALLATION ApplicationMetadataMessage_MessageType = 3 - ApplicationMetadataMessage_SYNC_INSTALLATION ApplicationMetadataMessage_MessageType = 4 -) - -var ApplicationMetadataMessage_MessageType_name = map[int32]string{ - 0: "TEXT_MESSAGE", - 1: "CONTACT_REQUEST", - 2: "MEMBERSHIP_UPDATE", - 3: "PAIR_INSTALLATION", - 4: "SYNC_INSTALLATION", -} - -var ApplicationMetadataMessage_MessageType_value = map[string]int32{ - "TEXT_MESSAGE": 0, - "CONTACT_REQUEST": 1, - "MEMBERSHIP_UPDATE": 2, - "PAIR_INSTALLATION": 3, - "SYNC_INSTALLATION": 4, -} - -func (x ApplicationMetadataMessage_MessageType) String() string { - return proto.EnumName(ApplicationMetadataMessage_MessageType_name, int32(x)) -} - -func (ApplicationMetadataMessage_MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{2, 0} -} - -type StickerMessage struct { - Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` - Pack int32 `protobuf:"varint,2,opt,name=pack,proto3" json:"pack,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StickerMessage) Reset() { *m = StickerMessage{} } -func (m *StickerMessage) String() string { return proto.CompactTextString(m) } -func (*StickerMessage) ProtoMessage() {} -func (*StickerMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{0} -} - -func (m *StickerMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_StickerMessage.Unmarshal(m, b) -} -func (m *StickerMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_StickerMessage.Marshal(b, m, deterministic) -} -func (m *StickerMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_StickerMessage.Merge(m, src) -} -func (m *StickerMessage) XXX_Size() int { - return xxx_messageInfo_StickerMessage.Size(m) -} -func (m *StickerMessage) XXX_DiscardUnknown() { - xxx_messageInfo_StickerMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_StickerMessage proto.InternalMessageInfo - -func (m *StickerMessage) GetHash() string { - if m != nil { - return m.Hash - } - return "" -} - -func (m *StickerMessage) GetPack() int32 { - if m != nil { - return m.Pack - } - return 0 -} - -type ChatMessage struct { - // Lamport timestamp of the chat message - Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` - // Unix timestamps in milliseconds, currently not used as we use whisper as more reliable, but here - // so that we don't rely on it - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // Text of the message - Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"` - // Id of the message that we are replying to - ResponseTo string `protobuf:"bytes,4,opt,name=response_to,json=responseTo,proto3" json:"response_to,omitempty"` - // Ens name of the sender - EnsName string `protobuf:"bytes,5,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` - // Chat id, this field is symmetric for public-chats and private group chats, - // but asymmetric in case of one-to-ones, as the sender will use the chat-id - // of the received, while the receiver will use the chat-id of the sender. - // Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order - ChatId string `protobuf:"bytes,6,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` - // The type of message (public/one-to-one/private-group-chat) - MessageType ChatMessage_MessageType `protobuf:"varint,7,opt,name=message_type,json=messageType,proto3,enum=protobuf.ChatMessage_MessageType" json:"message_type,omitempty"` - // The type of the content of the message - ContentType ChatMessage_ContentType `protobuf:"varint,8,opt,name=content_type,json=contentType,proto3,enum=protobuf.ChatMessage_ContentType" json:"content_type,omitempty"` - // Types that are valid to be assigned to Payload: - // *ChatMessage_Sticker - Payload isChatMessage_Payload `protobuf_oneof:"payload"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ChatMessage) Reset() { *m = ChatMessage{} } -func (m *ChatMessage) String() string { return proto.CompactTextString(m) } -func (*ChatMessage) ProtoMessage() {} -func (*ChatMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{1} -} - -func (m *ChatMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ChatMessage.Unmarshal(m, b) -} -func (m *ChatMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ChatMessage.Marshal(b, m, deterministic) -} -func (m *ChatMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChatMessage.Merge(m, src) -} -func (m *ChatMessage) XXX_Size() int { - return xxx_messageInfo_ChatMessage.Size(m) -} -func (m *ChatMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ChatMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ChatMessage proto.InternalMessageInfo - -func (m *ChatMessage) GetClock() uint64 { - if m != nil { - return m.Clock - } - return 0 -} - -func (m *ChatMessage) GetTimestamp() uint64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func (m *ChatMessage) GetText() string { - if m != nil { - return m.Text - } - return "" -} - -func (m *ChatMessage) GetResponseTo() string { - if m != nil { - return m.ResponseTo - } - return "" -} - -func (m *ChatMessage) GetEnsName() string { - if m != nil { - return m.EnsName - } - return "" -} - -func (m *ChatMessage) GetChatId() string { - if m != nil { - return m.ChatId - } - return "" -} - -func (m *ChatMessage) GetMessageType() ChatMessage_MessageType { - if m != nil { - return m.MessageType - } - return ChatMessage_ONE_TO_ONE -} - -func (m *ChatMessage) GetContentType() ChatMessage_ContentType { - if m != nil { - return m.ContentType - } - return ChatMessage_TEXT_PLAIN -} - -type isChatMessage_Payload interface { - isChatMessage_Payload() -} - -type ChatMessage_Sticker struct { - Sticker *StickerMessage `protobuf:"bytes,9,opt,name=sticker,proto3,oneof"` -} - -func (*ChatMessage_Sticker) isChatMessage_Payload() {} - -func (m *ChatMessage) GetPayload() isChatMessage_Payload { - if m != nil { - return m.Payload - } - return nil -} - -func (m *ChatMessage) GetSticker() *StickerMessage { - if x, ok := m.GetPayload().(*ChatMessage_Sticker); ok { - return x.Sticker - } - return nil -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*ChatMessage) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*ChatMessage_Sticker)(nil), - } -} - -type ApplicationMetadataMessage struct { - // Signature of the payload field - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - // This is the encoded protobuf of the application level message, i.e ChatMessage - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` - // The type of protobuf message sent - MessageType ApplicationMetadataMessage_MessageType `protobuf:"varint,3,opt,name=message_type,json=messageType,proto3,enum=protobuf.ApplicationMetadataMessage_MessageType" json:"message_type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplicationMetadataMessage) Reset() { *m = ApplicationMetadataMessage{} } -func (m *ApplicationMetadataMessage) String() string { return proto.CompactTextString(m) } -func (*ApplicationMetadataMessage) ProtoMessage() {} -func (*ApplicationMetadataMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{2} -} - -func (m *ApplicationMetadataMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplicationMetadataMessage.Unmarshal(m, b) -} -func (m *ApplicationMetadataMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplicationMetadataMessage.Marshal(b, m, deterministic) -} -func (m *ApplicationMetadataMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplicationMetadataMessage.Merge(m, src) -} -func (m *ApplicationMetadataMessage) XXX_Size() int { - return xxx_messageInfo_ApplicationMetadataMessage.Size(m) -} -func (m *ApplicationMetadataMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ApplicationMetadataMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplicationMetadataMessage proto.InternalMessageInfo - -func (m *ApplicationMetadataMessage) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *ApplicationMetadataMessage) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func (m *ApplicationMetadataMessage) GetMessageType() ApplicationMetadataMessage_MessageType { - if m != nil { - return m.MessageType - } - return ApplicationMetadataMessage_TEXT_MESSAGE -} - -func init() { - proto.RegisterEnum("protobuf.ChatMessage_MessageType", ChatMessage_MessageType_name, ChatMessage_MessageType_value) - proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value) - proto.RegisterEnum("protobuf.ApplicationMetadataMessage_MessageType", ApplicationMetadataMessage_MessageType_name, ApplicationMetadataMessage_MessageType_value) - proto.RegisterType((*StickerMessage)(nil), "protobuf.StickerMessage") - proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage") - proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") -} - -func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) } - -var fileDescriptor_33c57e4bae7b9afd = []byte{ - // 563 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x5f, 0x6f, 0xda, 0x3e, - 0x14, 0x25, 0x10, 0xa0, 0xdc, 0xd0, 0xfe, 0x5c, 0xff, 0x36, 0x2d, 0x9b, 0x2a, 0x8d, 0xf1, 0xc4, - 0x13, 0x9a, 0xba, 0x3d, 0xec, 0x35, 0x4d, 0xad, 0x36, 0x1b, 0xf9, 0x33, 0xdb, 0x4c, 0xeb, 0x93, - 0xe5, 0x06, 0xaf, 0xa0, 0x96, 0x24, 0x22, 0xae, 0x34, 0xbe, 0xcf, 0x3e, 0xc9, 0x3e, 0xd9, 0x14, - 0x07, 0x0a, 0x4c, 0xea, 0x9e, 0xec, 0x73, 0x7c, 0x7d, 0xee, 0xbd, 0xe7, 0x5e, 0x38, 0x5e, 0xaa, - 0xb2, 0x94, 0x77, 0x6a, 0x5c, 0xac, 0x72, 0x9d, 0xe3, 0x23, 0x73, 0xdc, 0x3e, 0xfe, 0x18, 0x7e, - 0x82, 0x13, 0xa6, 0x17, 0xe9, 0xbd, 0x5a, 0x85, 0x75, 0x04, 0xc6, 0x60, 0xcf, 0x65, 0x39, 0x77, - 0xad, 0x81, 0x35, 0xea, 0x51, 0x73, 0xaf, 0xb8, 0x42, 0xa6, 0xf7, 0x6e, 0x73, 0x60, 0x8d, 0xda, - 0xd4, 0xdc, 0x87, 0xbf, 0x6d, 0x70, 0xfc, 0xb9, 0xd4, 0xdb, 0x7f, 0x2f, 0xa0, 0x9d, 0x3e, 0xe4, - 0xe9, 0xbd, 0xf9, 0x68, 0xd3, 0x1a, 0xe0, 0x33, 0xe8, 0xe9, 0xc5, 0x52, 0x95, 0x5a, 0x2e, 0x0b, - 0xf3, 0xdd, 0xa6, 0x3b, 0xa2, 0xd2, 0xd5, 0xea, 0xa7, 0x76, 0x5b, 0x75, 0xae, 0xea, 0x8e, 0xdf, - 0x82, 0xb3, 0x52, 0x65, 0x91, 0x67, 0xa5, 0x12, 0x3a, 0x77, 0x6d, 0xf3, 0x04, 0x5b, 0x8a, 0xe7, - 0xf8, 0x35, 0x1c, 0xa9, 0xac, 0x14, 0x99, 0x5c, 0x2a, 0xb7, 0x6d, 0x5e, 0xbb, 0x2a, 0x2b, 0x23, - 0xb9, 0x54, 0xf8, 0x15, 0x74, 0xd3, 0xb9, 0xd4, 0x62, 0x31, 0x73, 0x3b, 0xe6, 0xa5, 0x53, 0xc1, - 0x60, 0x86, 0x2f, 0xa1, 0xbf, 0x71, 0x40, 0xe8, 0x75, 0xa1, 0xdc, 0xee, 0xc0, 0x1a, 0x9d, 0x9c, - 0xbf, 0x1b, 0x6f, 0x7d, 0x18, 0xef, 0x75, 0x32, 0xde, 0x9c, 0x7c, 0x5d, 0x28, 0xea, 0x2c, 0x77, - 0xa0, 0x52, 0x49, 0xf3, 0x4c, 0xab, 0x4c, 0xd7, 0x2a, 0x47, 0xff, 0x52, 0xf1, 0xeb, 0xc8, 0x5a, - 0x25, 0xdd, 0x01, 0xfc, 0x11, 0xba, 0x65, 0x6d, 0xb9, 0xdb, 0x1b, 0x58, 0x23, 0xe7, 0xdc, 0xdd, - 0x09, 0x1c, 0xce, 0xe2, 0xba, 0x41, 0xb7, 0xa1, 0xc3, 0x19, 0x38, 0x7b, 0x75, 0xe1, 0x13, 0x80, - 0x38, 0x22, 0x82, 0xc7, 0x22, 0x8e, 0x08, 0x6a, 0x60, 0x04, 0xfd, 0x64, 0x7a, 0x31, 0x09, 0x7c, - 0x71, 0x45, 0xe3, 0x69, 0x82, 0x2c, 0x7c, 0x0a, 0xc7, 0x09, 0x0d, 0xbe, 0x79, 0x9c, 0x6c, 0xa8, - 0x26, 0x1e, 0xc0, 0x19, 0xbb, 0x61, 0x9c, 0x84, 0x22, 0x24, 0x8c, 0x79, 0x57, 0x44, 0x1c, 0x46, - 0xb4, 0x86, 0x29, 0x38, 0x7b, 0x75, 0x57, 0x59, 0x38, 0xf9, 0xce, 0x45, 0x32, 0xf1, 0x82, 0x08, - 0x35, 0xb0, 0x03, 0x5d, 0xc6, 0x03, 0xff, 0x0b, 0xa1, 0xc8, 0xc2, 0x00, 0x1d, 0xc6, 0x3d, 0x3e, - 0x65, 0xa8, 0x89, 0x7b, 0xd0, 0x26, 0x61, 0xfc, 0x39, 0x40, 0xad, 0x2a, 0xc6, 0x8f, 0xc3, 0xd0, - 0x8b, 0x2e, 0x91, 0x8d, 0xff, 0x87, 0xff, 0x36, 0x40, 0x50, 0xf2, 0x75, 0x4a, 0x18, 0x47, 0xed, - 0x8b, 0x1e, 0x74, 0x0b, 0xb9, 0x7e, 0xc8, 0xe5, 0x6c, 0xf8, 0xab, 0x09, 0x6f, 0xbc, 0xa2, 0x78, - 0x58, 0xa4, 0x52, 0x2f, 0xf2, 0x2c, 0x54, 0x5a, 0xce, 0xa4, 0x96, 0xdb, 0x9d, 0x3a, 0x83, 0x5e, - 0xb9, 0xb8, 0xcb, 0xa4, 0x7e, 0x5c, 0x29, 0xb3, 0x57, 0x7d, 0xba, 0x23, 0xb0, 0xfb, 0xa4, 0x63, - 0x36, 0xab, 0x4f, 0xb7, 0x10, 0xb3, 0xbf, 0xc6, 0xdd, 0x32, 0x83, 0x7a, 0xbf, 0xf3, 0xf9, 0xf9, - 0x9c, 0xcf, 0x4e, 0x7f, 0xb8, 0x3e, 0x9c, 0x00, 0x82, 0xbe, 0xf1, 0x66, 0x63, 0x25, 0x6a, 0xd4, - 0xcd, 0x46, 0xdc, 0xf3, 0xf9, 0x53, 0xb3, 0x16, 0x7e, 0x09, 0xa7, 0x21, 0x09, 0x2f, 0x08, 0x65, - 0xd7, 0x41, 0x22, 0xa6, 0xc9, 0xa5, 0xc7, 0x09, 0x6a, 0x56, 0x74, 0xe2, 0x05, 0x54, 0x04, 0x11, - 0xe3, 0xde, 0x64, 0xe2, 0xf1, 0x20, 0x8e, 0x50, 0xab, 0xa2, 0xd9, 0x4d, 0xe4, 0x1f, 0xd2, 0xf6, - 0x6d, 0xc7, 0x14, 0xfe, 0xe1, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf6, 0xa4, 0x11, 0xfe, 0xc7, - 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/status-im/status-go/protocol/protobuf/service.go b/vendor/github.com/status-im/status-go/protocol/protobuf/service.go index 5724ba066..56d5d54c2 100644 --- a/vendor/github.com/status-im/status-go/protocol/protobuf/service.go +++ b/vendor/github.com/status-im/status-go/protocol/protobuf/service.go @@ -4,7 +4,7 @@ import ( "github.com/golang/protobuf/proto" ) -//go:generate protoc --go_out=. ./message.proto +//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) { var message ApplicationMetadataMessage diff --git a/vendor/github.com/status-im/status-go/protocol/v1/clock.go b/vendor/github.com/status-im/status-go/protocol/v1/clock.go index bf914951e..652b3dcd4 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/clock.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/clock.go @@ -2,17 +2,17 @@ package protocol import "time" -const clockBumpInMs = int64(time.Minute / time.Millisecond) +const clockBumpInMs = uint64(time.Minute / time.Millisecond) // CalcMessageClock calculates a new clock value for Message. // It is used to properly sort messages and accommodate the fact // that time might be different on each device. -func CalcMessageClock(lastObservedValue int64, timeInMs TimestampInMs) int64 { +func CalcMessageClock(lastObservedValue uint64, timeInMs uint64) uint64 { clock := lastObservedValue - if clock < int64(timeInMs) { + if clock < timeInMs { // Added time should be larger than time skew tollerance for a message. // Here, we use 1 minute which is larger than accepted message time skew by Whisper. - clock = int64(timeInMs) + clockBumpInMs + clock = timeInMs + clockBumpInMs } else { clock++ } diff --git a/vendor/github.com/status-im/status-go/protocol/v1/decoder.go b/vendor/github.com/status-im/status-go/protocol/v1/decoder.go index 801011e21..efe29681a 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/decoder.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/decoder.go @@ -1,7 +1,6 @@ package protocol import ( - "container/list" "errors" "fmt" "io" @@ -15,14 +14,12 @@ import ( func NewMessageDecoder(r io.Reader) *transit.Decoder { decoder := transit.NewDecoder(r) decoder.AddHandler(pairMessageTag, pairMessageHandler) - decoder.AddHandler(membershipUpdateTag, membershipUpdateMessageHandler) return decoder } const ( - messageTag = "c4" - pairMessageTag = "p2" - membershipUpdateTag = "g5" + messageTag = "c4" + pairMessageTag = "p2" ) func pairMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { @@ -60,83 +57,6 @@ func pairMessageHandler(d transit.Decoder, value interface{}) (interface{}, erro return pm, nil } -func membershipUpdateMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { - taggedValue, ok := value.(transit.TaggedValue) - if !ok { - return nil, errors.New("not a tagged value") - } - values, ok := taggedValue.Value.([]interface{}) - if !ok { - return nil, errors.New("tagged value does not contain values") - } - - m := MembershipUpdateMessage{} - for idx, v := range values { - var ok bool - - switch idx { - case 0: - m.ChatID, ok = v.(string) - case 1: - var updates *list.List - updates, ok = v.(*list.List) - if !ok { - break - } - for e := updates.Front(); e != nil; e = e.Next() { - var value map[interface{}]interface{} - value, ok = e.Value.(map[interface{}]interface{}) - if !ok { - break - } - - update := MembershipUpdate{} - - update.ChatID, ok = value[transit.Keyword("chat-id")].(string) - if !ok { - break - } - update.Signature, ok = value[transit.Keyword("signature")].(string) - if !ok { - break - } - - // parse events - var events []interface{} - events, ok = value[transit.Keyword("events")].([]interface{}) - if !ok { - break - } - for _, item := range events { - var event map[interface{}]interface{} - event, ok = item.(map[interface{}]interface{}) - if !ok { - break - } - - var updateEvent MembershipUpdateEvent - updateEvent, ok = parseEvent(event) - if !ok { - break - } - - update.Events = append(update.Events, updateEvent) - } - - m.Updates = append(m.Updates, update) - } - default: - // skip any other values - ok = true - } - - if !ok { - return nil, fmt.Errorf("invalid value for index: %d", idx) - } - } - return m, nil -} - func setToString(set *transit.Set) ([]string, bool) { result := make([]string, 0, len(set.Contents)) for _, item := range set.Contents { @@ -148,43 +68,3 @@ func setToString(set *transit.Set) ([]string, bool) { } return result, true } - -func parseEvent(event map[interface{}]interface{}) (result MembershipUpdateEvent, ok bool) { - // Type is required - result.Type, ok = event[transit.Keyword("type")].(string) - if !ok { - return - } - // ClockValue is required - result.ClockValue, ok = event[transit.Keyword("clock-value")].(int64) - if !ok { - return - } - // Name is optional - if val, exists := event[transit.Keyword("name")]; exists { - result.Name, ok = val.(string) - if !ok { - return - } - } - // Member is optional - if val, exists := event[transit.Keyword("member")]; exists { - result.Member, ok = val.(string) - if !ok { - return - } - } - // Members is optional - if val, exists := event[transit.Keyword("members")]; exists { - var members *transit.Set - members, ok = val.(*transit.Set) - if !ok { - return - } - result.Members, ok = setToString(members) - if !ok { - return - } - } - return -} diff --git a/vendor/github.com/status-im/status-go/protocol/v1/encoder.go b/vendor/github.com/status-im/status-go/protocol/v1/encoder.go index daf0755ef..b0678cea1 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/encoder.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/encoder.go @@ -1,7 +1,6 @@ package protocol import ( - "container/list" "errors" "io" "reflect" @@ -10,8 +9,7 @@ import ( ) var ( - pairMessageType = reflect.TypeOf(PairMessage{}) - membershipUpdateType = reflect.TypeOf(MembershipUpdateMessage{}) + pairMessageType = reflect.TypeOf(PairMessage{}) defaultMessageValueEncoder = &messageValueEncoder{} ) @@ -22,7 +20,6 @@ var ( func NewMessageEncoder(w io.Writer) *transit.Encoder { encoder := transit.NewEncoder(w, false) encoder.AddHandler(pairMessageType, defaultMessageValueEncoder) - encoder.AddHandler(membershipUpdateType, defaultMessageValueEncoder) return encoder } @@ -45,47 +42,6 @@ func (messageValueEncoder) Encode(e transit.Encoder, value reflect.Value, asStri }, } return e.EncodeInterface(taggedValue, false) - case MembershipUpdateMessage: - updatesList := list.New() - for _, update := range message.Updates { - var events []interface{} - for _, event := range update.Events { - eventMap := map[interface{}]interface{}{ - transit.Keyword("type"): event.Type, - transit.Keyword("clock-value"): event.ClockValue, - } - if event.Name != "" { - eventMap[transit.Keyword("name")] = event.Name - } - if event.Member != "" { - eventMap[transit.Keyword("member")] = event.Member - } - if len(event.Members) > 0 { - members := make([]interface{}, len(event.Members)) - for idx, m := range event.Members { - members[idx] = m - } - eventMap[transit.Keyword("members")] = transit.NewSet(members) - } - events = append(events, eventMap) - } - - element := map[interface{}]interface{}{ - transit.Keyword("chat-id"): update.ChatID, - transit.Keyword("events"): events, - transit.Keyword("signature"): update.Signature, - } - updatesList.PushBack(element) - } - value := []interface{}{ - message.ChatID, - updatesList, - } - taggedValue := transit.TaggedValue{ - Tag: membershipUpdateTag, - Value: value, - } - return e.EncodeInterface(taggedValue, false) } return errors.New("unknown message type to encode") diff --git a/vendor/github.com/status-im/status-go/protocol/v1/membership_update_message.go b/vendor/github.com/status-im/status-go/protocol/v1/membership_update_message.go index 7d92a1be3..1a04065e8 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/membership_update_message.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/membership_update_message.go @@ -3,145 +3,147 @@ package protocol import ( "bytes" "crypto/ecdsa" - "encoding/hex" - "encoding/json" "fmt" - "reflect" "sort" "strings" "time" + "github.com/golang/protobuf/proto" "github.com/google/uuid" "github.com/pkg/errors" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" -) - -const ( - MembershipUpdateChatCreated = "chat-created" - MembershipUpdateNameChanged = "name-changed" - MembershipUpdateMembersAdded = "members-added" - MembershipUpdateMemberJoined = "member-joined" - MembershipUpdateMemberRemoved = "member-removed" - MembershipUpdateAdminsAdded = "admins-added" - MembershipUpdateAdminRemoved = "admin-removed" + "github.com/status-im/status-go/protocol/protobuf" ) // MembershipUpdateMessage is a message used to propagate information // about group membership changes. // For more information, see https://github.com/status-im/specs/blob/master/status-group-chats-spec.md. type MembershipUpdateMessage struct { - ChatID string `json:"chatId"` // UUID concatenated with hex-encoded public key of the creator for the chat - Updates []MembershipUpdate `json:"updates"` + ChatID string `json:"chatId"` // UUID concatenated with hex-encoded public key of the creator for the chat + Events []MembershipUpdateEvent `json:"events"` + Message *protobuf.ChatMessage `json:"-"` } -// Verify makes sure that the received update message has a valid signature. -// It also extracts public key from the signature available as From field. -// It does not verify the updates and their events. This should be done -// separately using Group struct. -func (m *MembershipUpdateMessage) Verify() error { - for idx, update := range m.Updates { - if err := update.extractFrom(); err != nil { - return errors.Wrapf(err, "failed to extract an author of %d update", idx) - } - m.Updates[idx] = update +const signatureLength = 65 + +func MembershipUpdateEventFromProtobuf(chatID string, raw []byte) (*MembershipUpdateEvent, error) { + if len(raw) <= signatureLength { + return nil, errors.New("invalid payload length") } - return nil -} + decodedEvent := protobuf.MembershipUpdateEvent{} + signature := raw[:signatureLength] + encodedEvent := raw[signatureLength:] -// EncodeMembershipUpdateMessage encodes a MembershipUpdateMessage using Transit serialization. -func EncodeMembershipUpdateMessage(value MembershipUpdateMessage) ([]byte, error) { - var buf bytes.Buffer - encoder := NewMessageEncoder(&buf) - if err := encoder.Encode(value); err != nil { + signatureMaterial := append([]byte(chatID), encodedEvent...) + publicKey, err := crypto.ExtractSignature(signatureMaterial, signature) + if err != nil { + return nil, errors.Wrap(err, "failed to extract signature") + } + + from := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + + err = proto.Unmarshal(encodedEvent, &decodedEvent) + if err != nil { return nil, err } - return buf.Bytes(), nil + return &MembershipUpdateEvent{ + ClockValue: decodedEvent.Clock, + ChatID: chatID, + Members: decodedEvent.Members, + Name: decodedEvent.Name, + Type: decodedEvent.Type, + Signature: signature, + RawPayload: encodedEvent, + From: from, + }, nil } -type MembershipUpdate struct { - ChatID string `json:"chatId"` - Signature string `json:"signature"` // hex-encoded without 0x prefix - Events []MembershipUpdateEvent `json:"events"` - From string `json:"from"` // hex-encoded with 0x prefix +func (m *MembershipUpdateMessage) ToProtobuf() *protobuf.MembershipUpdateMessage { + var rawEvents [][]byte + for _, e := range m.Events { + var encodedEvent []byte + encodedEvent = append(encodedEvent, e.Signature...) + encodedEvent = append(encodedEvent, e.RawPayload...) + rawEvents = append(rawEvents, encodedEvent) + } + return &protobuf.MembershipUpdateMessage{ + ChatId: m.ChatID, + Events: rawEvents, + Message: m.Message, + } } -// Sign creates a signature from MembershipUpdateEvents -// and updates MembershipUpdate's signature. -// It follows the algorithm describe in the spec: -// https://github.com/status-im/specs/blob/master/status-group-chats-spec.md#signature. -func (u *MembershipUpdate) Sign(identity *ecdsa.PrivateKey) error { - signature, err := createMembershipUpdateSignature(u.ChatID, u.Events, identity) - if err != nil { - return err +func MembershipUpdateMessageFromProtobuf(raw *protobuf.MembershipUpdateMessage) (*MembershipUpdateMessage, error) { + var events []MembershipUpdateEvent + for _, e := range raw.Events { + verifiedEvent, err := MembershipUpdateEventFromProtobuf(raw.ChatId, e) + if err != nil { + return nil, err + } + events = append(events, *verifiedEvent) } - u.Signature = signature - return nil + return &MembershipUpdateMessage{ + ChatID: raw.ChatId, + Events: events, + Message: raw.Message, + }, nil } -func (u *MembershipUpdate) extractFrom() error { - content, err := stringifyMembershipUpdateEvents(u.ChatID, u.Events) - if err != nil { - return errors.Wrap(err, "failed to stringify events") - } - signatureBytes, err := hex.DecodeString(u.Signature) - if err != nil { - return errors.Wrap(err, "failed to decode signature") - } - publicKey, err := crypto.ExtractSignature(content, signatureBytes) - if err != nil { - return errors.Wrap(err, "failed to extract signature") - } - u.From = types.EncodeHex(crypto.FromECDSAPub(publicKey)) - return nil -} - -func (u *MembershipUpdate) Flat() []MembershipUpdateFlat { - result := make([]MembershipUpdateFlat, 0, len(u.Events)) - for _, event := range u.Events { - result = append(result, MembershipUpdateFlat{ - MembershipUpdateEvent: event, - ChatID: u.ChatID, - Signature: u.Signature, - From: u.From, - }) - } - return result +// EncodeMembershipUpdateMessage encodes a MembershipUpdateMessage using protobuf serialization. +func EncodeMembershipUpdateMessage(value MembershipUpdateMessage) ([]byte, error) { + return proto.Marshal(value.ToProtobuf()) } // MembershipUpdateEvent contains an event information. // Member and Members are hex-encoded values with 0x prefix. type MembershipUpdateEvent struct { - Type string `json:"type"` - ClockValue int64 `json:"clockValue"` - Member string `json:"member,omitempty"` // in "member-joined", "member-removed" and "admin-removed" events - Members []string `json:"members,omitempty"` // in "members-added" and "admins-added" events - Name string `json:"name,omitempty"` // name of the group chat + Type protobuf.MembershipUpdateEvent_EventType `json:"type"` + ClockValue uint64 `json:"clockValue"` + Members []string `json:"members,omitempty"` // in "members-added" and "admins-added" events + Name string `json:"name,omitempty"` // name of the group chat + From string + Signature []byte + ChatID string + RawPayload []byte } -func (u MembershipUpdateEvent) Equal(update MembershipUpdateEvent) bool { - return u.Type == update.Type && - u.ClockValue == update.ClockValue && - u.Member == update.Member && - stringSliceEquals(u.Members, update.Members) && - u.Name == update.Name +func (u *MembershipUpdateEvent) Equal(update MembershipUpdateEvent) bool { + return bytes.Compare(u.Signature, update.Signature) == 0 } -type MembershipUpdateFlat struct { - MembershipUpdateEvent - ChatID string `json:"chatId"` - Signature string `json:"signature"` - From string `json:"from"` +func (u *MembershipUpdateEvent) Sign(key *ecdsa.PrivateKey) error { + if len(u.ChatID) == 0 { + return errors.New("can't sign with empty chatID") + } + encodedEvent, err := proto.Marshal(u.ToProtobuf()) + if err != nil { + return err + } + u.RawPayload = encodedEvent + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, []byte(u.ChatID)...) + signatureMaterial = crypto.Keccak256(append(signatureMaterial, u.RawPayload...)) + signature, err := crypto.Sign(signatureMaterial, key) + + if err != nil { + return err + } + u.Signature = signature + u.From = types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)) + return nil } -func (u MembershipUpdateFlat) Equal(update MembershipUpdateFlat) bool { - return u.ChatID == update.ChatID && - u.Signature == update.Signature && - u.From == update.From && - u.MembershipUpdateEvent.Equal(update.MembershipUpdateEvent) +func (u *MembershipUpdateEvent) ToProtobuf() *protobuf.MembershipUpdateEvent { + return &protobuf.MembershipUpdateEvent{ + Clock: u.ClockValue, + Name: u.Name, + Members: u.Members, + Type: u.Type, + } } -func MergeFlatMembershipUpdates(dest []MembershipUpdateFlat, src []MembershipUpdateFlat) []MembershipUpdateFlat { +func MergeMembershipUpdateEvents(dest []MembershipUpdateEvent, src []MembershipUpdateEvent) []MembershipUpdateEvent { for _, update := range src { var exists bool for _, existing := range dest { @@ -157,166 +159,101 @@ func MergeFlatMembershipUpdates(dest []MembershipUpdateFlat, src []MembershipUpd return dest } -func NewChatCreatedEvent(name string, admin string, clock int64) MembershipUpdateEvent { +func NewChatCreatedEvent(name string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateChatCreated, - Name: name, - Member: admin, - ClockValue: clock, - } -} - -func NewNameChangedEvent(name string, clock int64) MembershipUpdateEvent { - return MembershipUpdateEvent{ - Type: MembershipUpdateNameChanged, + Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, Name: name, ClockValue: clock, } } -func NewMembersAddedEvent(members []string, clock int64) MembershipUpdateEvent { +func NewNameChangedEvent(name string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMembersAdded, + Type: protobuf.MembershipUpdateEvent_NAME_CHANGED, + Name: name, + ClockValue: clock, + } +} + +func NewMembersAddedEvent(members []string, clock uint64) MembershipUpdateEvent { + return MembershipUpdateEvent{ + Type: protobuf.MembershipUpdateEvent_MEMBERS_ADDED, Members: members, ClockValue: clock, } } -func NewMemberJoinedEvent(member string, clock int64) MembershipUpdateEvent { +func NewMemberJoinedEvent(clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMemberJoined, - Member: member, + Type: protobuf.MembershipUpdateEvent_MEMBER_JOINED, ClockValue: clock, } } -func NewAdminsAddedEvent(admins []string, clock int64) MembershipUpdateEvent { +func NewAdminsAddedEvent(admins []string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateAdminsAdded, + Type: protobuf.MembershipUpdateEvent_ADMINS_ADDED, Members: admins, ClockValue: clock, } } -func NewMemberRemovedEvent(member string, clock int64) MembershipUpdateEvent { +func NewMemberRemovedEvent(member string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateMemberRemoved, - Member: member, + Type: protobuf.MembershipUpdateEvent_MEMBER_REMOVED, + Members: []string{member}, ClockValue: clock, } } -func NewAdminRemovedEvent(admin string, clock int64) MembershipUpdateEvent { +func NewAdminRemovedEvent(admin string, clock uint64) MembershipUpdateEvent { return MembershipUpdateEvent{ - Type: MembershipUpdateAdminRemoved, - Member: admin, + Type: protobuf.MembershipUpdateEvent_ADMIN_REMOVED, + Members: []string{admin}, ClockValue: clock, } } -func stringifyMembershipUpdateEvents(chatID string, events []MembershipUpdateEvent) ([]byte, error) { - sort.Slice(events, func(i, j int) bool { - return events[i].ClockValue < events[j].ClockValue - }) - tuples := make([]interface{}, len(events)) - for idx, event := range events { - tuples[idx] = tupleMembershipUpdateEvent(event) - } - structureToSign := []interface{}{ - tuples, - chatID, - } - return json.Marshal(structureToSign) -} - -func createMembershipUpdateSignature(chatID string, events []MembershipUpdateEvent, identity *ecdsa.PrivateKey) (string, error) { - data, err := stringifyMembershipUpdateEvents(chatID, events) - if err != nil { - return "", err - } - return crypto.SignBytesAsHex(data, identity) -} - -var membershipUpdateEventFieldNamesCompat = map[string]string{ - "ClockValue": "clock-value", - "Name": "name", - "Type": "type", - "Member": "member", - "Members": "members", -} - -func tupleMembershipUpdateEvent(update MembershipUpdateEvent) [][]interface{} { - // Sort all slices first. - sort.Slice(update.Members, func(i, j int) bool { - return update.Members[i] < update.Members[j] - }) - v := reflect.ValueOf(update) - result := make([][]interface{}, 0, v.NumField()) - for i := 0; i < v.NumField(); i++ { - fieldName := v.Type().Field(i).Name - if name, exists := membershipUpdateEventFieldNamesCompat[fieldName]; exists { - fieldName = name - } - field := v.Field(i) - if !isZeroValue(field) { - result = append(result, []interface{}{fieldName, field.Interface()}) - } - } - // Sort the result lexicographically. - // We know that the first item of a tuple is a string - // because it's a field name. - sort.Slice(result, func(i, j int) bool { - return result[i][0].(string) < result[j][0].(string) - }) - return result -} - type Group struct { chatID string name string - updates []MembershipUpdateFlat + events []MembershipUpdateEvent admins *stringSet members *stringSet + joined *stringSet } func groupChatID(creator *ecdsa.PublicKey) string { return uuid.New().String() + "-" + types.EncodeHex(crypto.FromECDSAPub(creator)) } -func NewGroupWithMembershipUpdates(chatID string, updates []MembershipUpdate) (*Group, error) { - flatten := make([]MembershipUpdateFlat, 0, len(updates)) - for _, update := range updates { - flatten = append(flatten, update.Flat()...) - } - return newGroup(chatID, flatten) +func NewGroupWithEvents(chatID string, events []MembershipUpdateEvent) (*Group, error) { + return newGroup(chatID, events) } func NewGroupWithCreator(name string, creator *ecdsa.PrivateKey) (*Group, error) { chatID := groupChatID(&creator.PublicKey) - creatorHex := publicKeyToString(&creator.PublicKey) clock := TimestampInMsFromTime(time.Now()) - chatCreated := NewChatCreatedEvent(name, creatorHex, int64(clock)) - update := MembershipUpdate{ - ChatID: chatID, - From: creatorHex, - Events: []MembershipUpdateEvent{chatCreated}, - } - if err := update.Sign(creator); err != nil { + chatCreated := NewChatCreatedEvent(name, clock) + chatCreated.ChatID = chatID + err := chatCreated.Sign(creator) + if err != nil { return nil, err } - return newGroup(chatID, update.Flat()) + return newGroup(chatID, []MembershipUpdateEvent{chatCreated}) } -func NewGroup(chatID string, updates []MembershipUpdateFlat) (*Group, error) { - return newGroup(chatID, updates) +func NewGroup(chatID string, events []MembershipUpdateEvent) (*Group, error) { + return newGroup(chatID, events) } -func newGroup(chatID string, updates []MembershipUpdateFlat) (*Group, error) { +func newGroup(chatID string, events []MembershipUpdateEvent) (*Group, error) { g := Group{ chatID: chatID, - updates: updates, + events: events, admins: newStringSet(), members: newStringSet(), + joined: newStringSet(), } if err := g.init(); err != nil { return nil, err @@ -329,17 +266,17 @@ func (g *Group) init() error { var chatID string - for _, update := range g.updates { + for _, event := range g.events { if chatID == "" { - chatID = update.ChatID - } else if update.ChatID != chatID { + chatID = event.ChatID + } else if event.ChatID != chatID { return errors.New("updates contain different chat IDs") } - valid := g.validateEvent(update.From, update.MembershipUpdateEvent) + valid := g.validateEvent(event) if !valid { - return fmt.Errorf("invalid event %#+v from %s", update.MembershipUpdateEvent, update.From) + return fmt.Errorf("invalid event %#+v from %s", event, event.From) } - g.processEvent(update.From, update.MembershipUpdateEvent) + g.processEvent(event) } valid := g.validateChatID(g.chatID) @@ -357,8 +294,8 @@ func (g Group) ChatID() string { return g.chatID } -func (g Group) Updates() []MembershipUpdateFlat { - return g.updates +func (g Group) Events() []MembershipUpdateEvent { + return g.events } func (g Group) Name() string { @@ -374,18 +311,12 @@ func (g Group) Admins() []string { } func (g Group) Joined() []string { - var result []string - for _, update := range g.updates { - if update.Type == MembershipUpdateMemberJoined { - result = append(result, update.Member) - } - } - return result + return g.joined.List() } -func (g *Group) ProcessEvents(from *ecdsa.PublicKey, events []MembershipUpdateEvent) error { +func (g *Group) ProcessEvents(events []MembershipUpdateEvent) error { for _, event := range events { - err := g.ProcessEvent(from, event) + err := g.ProcessEvent(event) if err != nil { return err } @@ -393,38 +324,33 @@ func (g *Group) ProcessEvents(from *ecdsa.PublicKey, events []MembershipUpdateEv return nil } -func (g *Group) ProcessEvent(from *ecdsa.PublicKey, event MembershipUpdateEvent) error { - fromHex := types.EncodeHex(crypto.FromECDSAPub(from)) - if !g.validateEvent(fromHex, event) { - return fmt.Errorf("invalid event %#+v from %s", event, from) +func (g *Group) ProcessEvent(event MembershipUpdateEvent) error { + if !g.validateEvent(event) { + return fmt.Errorf("invalid event %#+v", event) } - update := MembershipUpdate{ - ChatID: g.chatID, - From: fromHex, - Events: []MembershipUpdateEvent{event}, - } - g.updates = append(g.updates, update.Flat()...) - g.processEvent(fromHex, event) + // Check if exists + g.events = append(g.events, event) + g.processEvent(event) return nil } -func (g Group) LastClockValue() int64 { - if len(g.updates) == 0 { +func (g Group) LastClockValue() uint64 { + if len(g.events) == 0 { return 0 } - return g.updates[len(g.updates)-1].ClockValue + return g.events[len(g.events)-1].ClockValue } -func (g Group) NextClockValue() int64 { +func (g Group) NextClockValue() uint64 { return g.LastClockValue() + 1 } func (g Group) creator() (string, error) { - if len(g.updates) == 0 { + if len(g.events) == 0 { return "", errors.New("no events in the group") } - first := g.updates[0] - if first.Type != MembershipUpdateChatCreated { + first := g.events[0] + if first.Type != protobuf.MembershipUpdateEvent_CHAT_CREATED { return "", fmt.Errorf("expected first event to be 'chat-created', got %s", first.Type) } return first.From, nil @@ -440,53 +366,63 @@ func (g Group) validateChatID(chatID string) bool { return strings.HasSuffix(chatID, creator) && chatID != creator } +func (g Group) IsMember(id string) bool { + return g.members.Has(id) +} + // validateEvent returns true if a given event is valid. -func (g Group) validateEvent(from string, event MembershipUpdateEvent) bool { +func (g Group) validateEvent(event MembershipUpdateEvent) bool { + if len(event.From) == 0 { + return false + } switch event.Type { - case MembershipUpdateChatCreated: + case protobuf.MembershipUpdateEvent_CHAT_CREATED: return g.admins.Empty() && g.members.Empty() - case MembershipUpdateNameChanged: - return g.admins.Has(from) && len(event.Name) > 0 - case MembershipUpdateMembersAdded: - return g.admins.Has(from) - case MembershipUpdateMemberJoined: - return g.members.Has(from) && from == event.Member - case MembershipUpdateMemberRemoved: + case protobuf.MembershipUpdateEvent_NAME_CHANGED: + return g.admins.Has(event.From) && len(event.Name) > 0 + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: + return g.admins.Has(event.From) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + return g.members.Has(event.From) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: // Member can remove themselves or admin can remove a member. - return from == event.Member || (g.admins.Has(from) && !g.admins.Has(event.Member)) - case MembershipUpdateAdminsAdded: - return g.admins.Has(from) && stringSliceSubset(event.Members, g.members.List()) - case MembershipUpdateAdminRemoved: - return g.admins.Has(from) && from == event.Member + return len(event.Members) == 1 && (event.From == event.Members[0] || (g.admins.Has(event.From) && !g.admins.Has(event.Members[0]))) + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: + return g.admins.Has(event.From) && stringSliceSubset(event.Members, g.members.List()) + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + return len(event.Members) == 1 && g.admins.Has(event.From) && event.From == event.Members[0] default: return false } } -func (g *Group) processEvent(from string, event MembershipUpdateEvent) { +func (g *Group) processEvent(event MembershipUpdateEvent) { switch event.Type { - case MembershipUpdateChatCreated: + case protobuf.MembershipUpdateEvent_CHAT_CREATED: g.name = event.Name - g.members.Add(event.Member) - g.admins.Add(event.Member) - case MembershipUpdateNameChanged: + g.members.Add(event.From) + g.joined.Add(event.From) + g.admins.Add(event.From) + case protobuf.MembershipUpdateEvent_NAME_CHANGED: g.name = event.Name - case MembershipUpdateAdminsAdded: + case protobuf.MembershipUpdateEvent_ADMINS_ADDED: g.admins.Add(event.Members...) - case MembershipUpdateAdminRemoved: - g.admins.Remove(event.Member) - case MembershipUpdateMembersAdded: + case protobuf.MembershipUpdateEvent_ADMIN_REMOVED: + g.admins.Remove(event.Members[0]) + case protobuf.MembershipUpdateEvent_MEMBERS_ADDED: g.members.Add(event.Members...) - case MembershipUpdateMemberRemoved: - g.members.Remove(event.Member) - case MembershipUpdateMemberJoined: - g.members.Add(event.Member) + case protobuf.MembershipUpdateEvent_MEMBER_REMOVED: + g.admins.Remove(event.Members[0]) + g.joined.Remove(event.Members[0]) + g.members.Remove(event.Members[0]) + case protobuf.MembershipUpdateEvent_MEMBER_JOINED: + g.joined.Add(event.From) } } func (g *Group) sortEvents() { - sort.Slice(g.updates, func(i, j int) bool { - return g.updates[i].ClockValue < g.updates[j].ClockValue + sort.Slice(g.events, func(i, j int) bool { + return g.events[i].ClockValue < g.events[j].ClockValue }) } diff --git a/vendor/github.com/status-im/status-go/protocol/v1/message.go b/vendor/github.com/status-im/status-go/protocol/v1/message.go index afffcc2ab..df7b7c37e 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/message.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/message.go @@ -17,34 +17,11 @@ var ( ErrInvalidDecodedValue = errors.New("invalid decoded value type") ) -// TimestampInMs is a timestamp in milliseconds. -type TimestampInMs int64 - -// Time returns a time.Time instance. -func (t TimestampInMs) Time() time.Time { - ts := int64(t) - seconds := ts / 1000 - return time.Unix(seconds, (ts%1000)*int64(time.Millisecond)) -} - // TimestampInMsFromTime returns a TimestampInMs from a time.Time instance. -func TimestampInMsFromTime(t time.Time) TimestampInMs { - return TimestampInMs(t.UnixNano() / int64(time.Millisecond)) +func TimestampInMsFromTime(t time.Time) uint64 { + return uint64(t.UnixNano() / int64(time.Millisecond)) } -// Flags define various boolean properties of a message. -type Flags uint64 - -func (f *Flags) Set(val Flags) { *f = *f | val } -func (f *Flags) Clear(val Flags) { *f = *f &^ val } -func (f *Flags) Toggle(val Flags) { *f = *f ^ val } -func (f Flags) Has(val Flags) bool { return f&val != 0 } - -// A list of Message flags. By default, a message is unread. -const ( - MessageRead Flags = 1 << iota -) - // MessageID calculates the messageID from author's compressed public key // and not encrypted but encoded payload. func MessageID(author *ecdsa.PublicKey, data []byte) types.HexBytes { @@ -53,7 +30,7 @@ func MessageID(author *ecdsa.PublicKey, data []byte) types.HexBytes { } // WrapMessageV1 wraps a payload into a protobuf message and signs it if an identity is provided -func WrapMessageV1(payload []byte, identity *ecdsa.PrivateKey) ([]byte, error) { +func WrapMessageV1(payload []byte, messageType protobuf.ApplicationMetadataMessage_Type, identity *ecdsa.PrivateKey) ([]byte, error) { var signature []byte if identity != nil { var err error @@ -65,6 +42,7 @@ func WrapMessageV1(payload []byte, identity *ecdsa.PrivateKey) ([]byte, error) { message := &protobuf.ApplicationMetadataMessage{ Signature: signature, + Type: messageType, Payload: payload, } return proto.Marshal(message) diff --git a/vendor/github.com/status-im/status-go/protocol/v1/status_message.go b/vendor/github.com/status-im/status-go/protocol/v1/status_message.go index 7d9cb9321..656509c1b 100644 --- a/vendor/github.com/status-im/status-go/protocol/v1/status_message.go +++ b/vendor/github.com/status-im/status-go/protocol/v1/status_message.go @@ -17,18 +17,12 @@ import ( type StatusMessageT int -const ( - MessageT StatusMessageT = iota + 1 - MembershipUpdateMessageT - PairMessageT -) - // StatusMessage is any Status Protocol message. type StatusMessage struct { // TransportMessage is the parsed message received from the transport layer, i.e the input TransportMessage *types.Message `json:"transportMessage"` - // MessageType is the type of application message contained - MessageType StatusMessageT `json:"-"` + // Type is the type of application message contained + Type protobuf.ApplicationMetadataMessage_Type `json:"-"` // ParsedMessage is the parsed message by the application layer, i.e the output ParsedMessage interface{} `json:"-"` @@ -157,23 +151,37 @@ func (m *StatusMessage) HandleApplicationMetadata() error { // Calculate ID using the wrapped record m.ID = MessageID(recoveredKey, m.DecryptedPayload) m.DecryptedPayload = message.Payload + m.Type = message.Type return nil } func (m *StatusMessage) HandleApplication() error { - // Try protobuf first - var message protobuf.ChatMessage + switch m.Type { + case protobuf.ApplicationMetadataMessage_CHAT_MESSAGE: + var message protobuf.ChatMessage - err := proto.Unmarshal(m.DecryptedPayload, &message) - if err != nil { - m.ParsedMessage = nil - log.Printf("[message::DecodeMessage] could not decode protobuf message: %#x, err: %v", m.Hash, err.Error()) - } else { - m.MessageType = MessageT - m.ParsedMessage = message + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode ChatMessage: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message - return nil + return nil + } + case protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE: + var message protobuf.MembershipUpdateMessage + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode MembershipUpdateMessage: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } } + return nil } diff --git a/vendor/golang.org/x/crypto/blowfish/block.go b/vendor/golang.org/x/crypto/blowfish/block.go new file mode 100644 index 000000000..9d80f1952 --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/block.go @@ -0,0 +1,159 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package blowfish + +// getNextWord returns the next big-endian uint32 value from the byte slice +// at the given position in a circular manner, updating the position. +func getNextWord(b []byte, pos *int) uint32 { + var w uint32 + j := *pos + for i := 0; i < 4; i++ { + w = w<<8 | uint32(b[j]) + j++ + if j >= len(b) { + j = 0 + } + } + *pos = j + return w +} + +// ExpandKey performs a key expansion on the given *Cipher. Specifically, it +// performs the Blowfish algorithm's key schedule which sets up the *Cipher's +// pi and substitution tables for calls to Encrypt. This is used, primarily, +// by the bcrypt package to reuse the Blowfish key schedule during its +// set up. It's unlikely that you need to use this directly. +func ExpandKey(key []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + // Using inlined getNextWord for performance. + var d uint32 + for k := 0; k < 4; k++ { + d = d<<8 | uint32(key[j]) + j++ + if j >= len(key) { + j = 0 + } + } + c.p[i] ^= d + } + + var l, r uint32 + for i := 0; i < 18; i += 2 { + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +// This is similar to ExpandKey, but folds the salt during the key +// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero +// salt passed in, reusing ExpandKey turns out to be a place of inefficiency +// and specializing it here is useful. +func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + c.p[i] ^= getNextWord(key, &j) + } + + j = 0 + var l, r uint32 + for i := 0; i < 18; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[0] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] + xr ^= c.p[17] + return xr, xl +} + +func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[17] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] + xr ^= c.p[0] + return xr, xl +} diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go new file mode 100644 index 000000000..213bf204a --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -0,0 +1,99 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm. +// +// Blowfish is a legacy cipher and its short block size makes it vulnerable to +// birthday bound attacks (see https://sweet32.info). It should only be used +// where compatibility with legacy systems, not security, is the goal. +// +// Deprecated: any new system should use AES (from crypto/aes, if necessary in +// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from +// golang.org/x/crypto/chacha20poly1305). +package blowfish // import "golang.org/x/crypto/blowfish" + +// The code is a port of Bruce Schneier's C implementation. +// See https://www.schneier.com/blowfish.html. + +import "strconv" + +// The Blowfish block size in bytes. +const BlockSize = 8 + +// A Cipher is an instance of Blowfish encryption using a particular key. +type Cipher struct { + p [18]uint32 + s0, s1, s2, s3 [256]uint32 +} + +type KeySizeError int + +func (k KeySizeError) Error() string { + return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) +} + +// NewCipher creates and returns a Cipher. +// The key argument should be the Blowfish key, from 1 to 56 bytes. +func NewCipher(key []byte) (*Cipher, error) { + var result Cipher + if k := len(key); k < 1 || k > 56 { + return nil, KeySizeError(k) + } + initCipher(&result) + ExpandKey(key, &result) + return &result, nil +} + +// NewSaltedCipher creates a returns a Cipher that folds a salt into its key +// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is +// sufficient and desirable. For bcrypt compatibility, the key can be over 56 +// bytes. +func NewSaltedCipher(key, salt []byte) (*Cipher, error) { + if len(salt) == 0 { + return NewCipher(key) + } + var result Cipher + if k := len(key); k < 1 { + return nil, KeySizeError(k) + } + initCipher(&result) + expandKeyWithSalt(key, salt, &result) + return &result, nil +} + +// BlockSize returns the Blowfish block size, 8 bytes. +// It is necessary to satisfy the Block interface in the +// package "crypto/cipher". +func (c *Cipher) BlockSize() int { return BlockSize } + +// Encrypt encrypts the 8-byte buffer src using the key k +// and stores the result in dst. +// Note that for amounts of data larger than a block, +// it is not safe to just call Encrypt on successive blocks; +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). +func (c *Cipher) Encrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = encryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +// Decrypt decrypts the 8-byte buffer src using the key k +// and stores the result in dst. +func (c *Cipher) Decrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = decryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +func initCipher(c *Cipher) { + copy(c.p[0:], p[0:]) + copy(c.s0[0:], s0[0:]) + copy(c.s1[0:], s1[0:]) + copy(c.s2[0:], s2[0:]) + copy(c.s3[0:], s3[0:]) +} diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go new file mode 100644 index 000000000..d04077595 --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/const.go @@ -0,0 +1,199 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The startup permutation array and substitution boxes. +// They are the hexadecimal digits of PI; see: +// https://www.schneier.com/code/constants.txt. + +package blowfish + +var s0 = [256]uint32{ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, +} + +var s1 = [256]uint32{ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, +} + +var s2 = [256]uint32{ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, + 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, + 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, + 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, + 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, + 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, + 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, + 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, + 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, + 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, + 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, + 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, + 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, + 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, + 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, + 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, + 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, + 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, + 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, +} + +var s3 = [256]uint32{ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +} + +var p = [18]uint32{ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f6383233f..369fb8981 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -28,7 +28,7 @@ github.com/davecgh/go-spew/spew github.com/deckarep/golang-set # github.com/edsrzf/mmap-go v1.0.0 github.com/edsrzf/mmap-go -# github.com/elastic/gosigar v0.10.5 +# github.com/elastic/gosigar v0.10.4 github.com/elastic/gosigar github.com/elastic/gosigar/sys/windows # github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7 @@ -109,7 +109,7 @@ github.com/ethereum/go-ethereum/trie # github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a github.com/fjl/memsize github.com/fjl/memsize/memsizeui -# github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 +# github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gballet/go-libpcsclite # github.com/go-playground/locales v0.13.0 github.com/go-playground/locales @@ -121,7 +121,7 @@ github.com/go-stack/stack # github.com/gogo/protobuf v1.3.0 github.com/gogo/protobuf/io github.com/gogo/protobuf/proto -# github.com/golang-migrate/migrate/v4 v4.7.0 +# github.com/golang-migrate/migrate/v4 v4.6.2 github.com/golang-migrate/migrate/v4 github.com/golang-migrate/migrate/v4/database github.com/golang-migrate/migrate/v4/internal/url @@ -133,7 +133,7 @@ github.com/golang/protobuf/proto github.com/golang/protobuf/protoc-gen-go/descriptor # github.com/golang/snappy v0.0.1 github.com/golang/snappy -# github.com/gomarkdown/markdown v0.0.0-20191113114344-af599402d015 => github.com/status-im/markdown v0.0.0-20191113114344-af599402d015 +# github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba github.com/gomarkdown/markdown github.com/gomarkdown/markdown/ast github.com/gomarkdown/markdown/parser @@ -178,7 +178,7 @@ github.com/jbenet/goprocess/periodic github.com/jbenet/goprocess/ratelimit # github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/jinzhu/copier -# github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 +# github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8 github.com/karalabe/usb # github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b github.com/koron/go-ssdp @@ -357,7 +357,7 @@ github.com/spaolacci/murmur3 github.com/status-im/doubleratchet # github.com/status-im/go-multiaddr-ethv4 v1.2.0 github.com/status-im/go-multiaddr-ethv4 -# github.com/status-im/keycard-go v0.0.0-20191119114148-6dd40a46baa0 +# github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 github.com/status-im/keycard-go/derivationpath # github.com/status-im/migrate/v4 v4.6.2-status.2 github.com/status-im/migrate/v4 @@ -369,7 +369,7 @@ github.com/status-im/migrate/v4/source/go_bindata github.com/status-im/rendezvous github.com/status-im/rendezvous/protocol github.com/status-im/rendezvous/server -# github.com/status-im/status-go/eth-node v0.0.0-20191126161717-86bc127b3d0a => ./eth-node +# github.com/status-im/status-go/eth-node v1.0.0 => ./eth-node github.com/status-im/status-go/eth-node/bridge/geth github.com/status-im/status-go/eth-node/bridge/geth/ens github.com/status-im/status-go/eth-node/crypto @@ -378,7 +378,7 @@ github.com/status-im/status-go/eth-node/types github.com/status-im/status-go/eth-node/types/ens # github.com/status-im/status-go/extkeys v1.0.0 => ./extkeys github.com/status-im/status-go/extkeys -# github.com/status-im/status-go/protocol v0.5.2 => ./protocol +# github.com/status-im/status-go/protocol v1.0.1 => ./protocol github.com/status-im/status-go/protocol github.com/status-im/status-go/protocol/datasync github.com/status-im/status-go/protocol/datasync/peer @@ -396,7 +396,7 @@ github.com/status-im/status-go/protocol/transport/whisper github.com/status-im/status-go/protocol/transport/whisper/migrations github.com/status-im/status-go/protocol/v1 github.com/status-im/status-go/protocol/zaputil -# github.com/status-im/status-go/whisper/v6 v6.0.0 => ./whisper +# github.com/status-im/status-go/whisper/v6 v6.0.1 => ./whisper github.com/status-im/status-go/whisper/v6 # github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 github.com/status-im/tcp-shaker @@ -500,7 +500,7 @@ golang.org/x/crypto/ssh/terminal # golang.org/x/lint v0.0.0-20190930215403-16217165b5de golang.org/x/lint golang.org/x/lint/golint -# golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 +# golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/net/bpf golang.org/x/net/context golang.org/x/net/html @@ -510,7 +510,7 @@ golang.org/x/net/idna golang.org/x/net/internal/iana golang.org/x/net/internal/socket golang.org/x/net/ipv4 -# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e +# golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sync/syncmap # golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 golang.org/x/sys/cpu