diff --git a/go.mod b/go.mod index 1f336489c..c87f0fa7e 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 v0.0.0-20191104174740-4d42851d4d5a => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba +replace github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/protocol => ./protocol @@ -23,7 +23,9 @@ replace github.com/status-im/status-go/waku => ./waku require ( github.com/beevik/ntp v0.2.0 github.com/ethereum/go-ethereum v1.9.5 + github.com/go-playground/universal-translator v0.17.0 // indirect github.com/golang/mock v1.3.1 + github.com/leodido/go-urn v1.2.0 // indirect github.com/lib/pq v1.2.0 github.com/libp2p/go-libp2p v0.4.2 // indirect github.com/libp2p/go-libp2p-core v0.2.4 @@ -33,6 +35,8 @@ require ( github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.2.1 + 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/migrate/v4 v4.6.2-status.2 github.com/status-im/rendezvous v1.3.0 @@ -46,6 +50,7 @@ require ( github.com/syndtr/goleveldb v1.0.0 go.uber.org/zap v1.13.0 golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c - gopkg.in/go-playground/validator.v9 v9.29.1 + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v9 v9.31.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index db992d3de..8ee4f0395 100644 --- a/go.sum +++ b/go.sum @@ -343,7 +343,6 @@ github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atq github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-libp2p v0.1.1 h1:52sB0TJuDk2nYMcMfHOKaPoaayDZjaYVCq6Vk1ejUTk= github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI= github.com/libp2p/go-libp2p v0.4.2 h1:p0cthB0jDNHO4gH2HzS8/nAMMXbfUlFHs0jwZ4U+F2g= github.com/libp2p/go-libp2p v0.4.2/go.mod h1:MNmgUxUw5pMsdOzMlT0EE7oKjRasl+WyVwM0IBlpKgQ= github.com/libp2p/go-libp2p-autonat v0.1.0 h1:aCWAu43Ri4nU0ZPO7NyLzUvvfqd0nE3dX0R/ZGYVgOU= @@ -356,7 +355,6 @@ github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUA github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= github.com/libp2p/go-libp2p-circuit v0.1.0 h1:eniLL3Y9aq/sryfyV1IAHj5rlvuyj3b7iz8tSiZpdhY= github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM= github.com/libp2p/go-libp2p-circuit v0.1.4 h1:Phzbmrg3BkVzbqd4ZZ149JxCuUWu2wZcXf/Kr6hZJj8= github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= @@ -364,7 +362,6 @@ github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvx github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= -github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A= github.com/libp2p/go-libp2p-core v0.2.4 h1:Et6ykkTwI6PU44tr8qUF9k43vP0aduMNniShAbUJJw8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -504,7 +501,6 @@ github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2 h1:/Bbsgsy3R6e3jf2qBahzNHzww6usYaZ0NhNH3sqdFS8= 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-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= github.com/multiformats/go-multiaddr-fmt v0.0.1 h1:5YjeOIzbX8OTKVaN72aOzGIYW7PnrZrnkDyOfAWRSMA= @@ -659,7 +655,6 @@ github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T6 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= github.com/status-im/rendezvous v1.3.0/go.mod h1:+hzjuP+j/XzLPeF6E50b88pWOTLdTcwjvNYt+Gh1W1s= -github.com/status-im/status-go v0.38.5/go.mod h1:UKxySGdqFuVPvCyPSYD0+zKeV2OzDijZqICvR+tZ3uM= github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 h1:oa0KU5jJRNtXaM/P465MhvSFo/HM2O8qi2DDuPcd7ro= github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= @@ -754,7 +749,6 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE= golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -787,9 +781,8 @@ 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-20190912160710-24e19bdeb0f2 h1:4dVFTC832rPn4pomLSz1vA+are2+dU19w1H8OngV7nc= golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/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= @@ -851,8 +844,6 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191213032237-7093a17b0467 h1:Jybbe55FT+YYZIJGWmJIA4ZGcglFuZOduakIW3+gHXY= -golang.org/x/tools v0.0.0-20191213032237-7093a17b0467/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= @@ -885,8 +876,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= +gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= diff --git a/protocol/chat.go b/protocol/chat.go index 74600b64d..08988daca 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -75,6 +75,10 @@ func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) { } +func (c *Chat) Public() bool { + return c.ChatType == ChatTypePublic +} + func (c *Chat) MarshalJSON() ([]byte, error) { type ChatAlias Chat item := struct { @@ -212,20 +216,22 @@ func OneToOneFromPublicKey(pk *ecdsa.PublicKey) *Chat { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey) Chat { return Chat{ - ID: oneToOneChatID(publicKey), - Name: name, - Active: true, - ChatType: ChatTypeOneToOne, + ID: oneToOneChatID(publicKey), + Name: name, + Timestamp: int64(timestampInMs()), + Active: true, + ChatType: ChatTypeOneToOne, } } func CreatePublicChat(name string) Chat { return Chat{ - ID: name, - Name: name, - Active: true, - Color: chatColors[rand.Intn(len(chatColors))], - ChatType: ChatTypePublic, + ID: name, + Name: name, + Active: true, + Timestamp: int64(timestampInMs()), + Color: chatColors[rand.Intn(len(chatColors))], + ChatType: ChatTypePublic, } } diff --git a/protocol/contact.go b/protocol/contact.go index cc6c2769f..6f42f6be6 100644 --- a/protocol/contact.go +++ b/protocol/contact.go @@ -106,3 +106,8 @@ func buildContact(publicKey *ecdsa.PublicKey) (*Contact, error) { return contact, nil } + +func contactIDFromPublicKey(key *ecdsa.PublicKey) string { + return types.EncodeHex(crypto.FromECDSAPub(key)) + +} diff --git a/protocol/go.mod b/protocol/go.mod index 9c69bc4e1..a9010509b 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 v0.0.0-20191104174740-4d42851d4d5a => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba +replace github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/eth-node => ../eth-node @@ -14,7 +14,7 @@ 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-20191104174740-4d42851d4d5a + 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 @@ -23,9 +23,8 @@ require ( 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 v0.38.5 // indirect - github.com/status-im/status-go/eth-node v1.0.0 - github.com/status-im/status-go/whisper/v6 v6.0.1 + github.com/status-im/status-go/eth-node v1.1.0 + github.com/status-im/status-go/whisper/v6 v6.1.0 github.com/stretchr/testify v1.4.0 github.com/vacp2p/mvds v0.0.23 go.uber.org/zap v1.13.0 diff --git a/protocol/go.sum b/protocol/go.sum index 050eb91f7..7620481b2 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -156,9 +156,11 @@ github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zac 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= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= 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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -196,6 +198,7 @@ github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a h1:DsPLKbIJTzH github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -289,6 +292,7 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= @@ -763,6 +767,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191213032237-7093a17b0467 h1:Jybbe55FT+YYZIJGWmJIA4ZGcglFuZOduakIW3+gHXY= golang.org/x/tools v0.0.0-20191213032237-7093a17b0467/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= diff --git a/protocol/message.go b/protocol/message.go index 279833639..a58610a6a 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -113,6 +113,10 @@ type RawMessage struct { func (m *Message) MarshalJSON() ([]byte, error) { type MessageAlias Message + type StickerAlias struct { + Hash string `json:"hash"` + Pack int32 `json:"pack"` + } item := struct { ID string `json:"id"` WhisperTimestamp uint64 `json:"whisperTimestamp"` @@ -132,7 +136,7 @@ func (m *Message) MarshalJSON() ([]byte, error) { Replace string `json:"replace,omitEmpty"` ResponseTo string `json:"responseTo"` EnsName string `json:"ensName"` - Sticker *protobuf.StickerMessage `json:"sticker"` + Sticker *StickerAlias `json:"sticker"` CommandParameters *CommandParameters `json:"commandParameters"` Timestamp uint64 `json:"timestamp"` ContentType protobuf.ChatMessage_ContentType `json:"contentType"` @@ -159,10 +163,15 @@ func (m *Message) MarshalJSON() ([]byte, error) { Timestamp: m.Timestamp, ContentType: m.ContentType, MessageType: m.MessageType, - Sticker: m.GetSticker(), CommandParameters: m.CommandParameters, } + if sticker := m.GetSticker(); sticker != nil { + item.Sticker = &StickerAlias{ + Pack: sticker.Pack, + Hash: sticker.Hash, + } + } return json.Marshal(item) } diff --git a/protocol/message_handler.go b/protocol/message_handler.go index bbcfcd202..d051f22e3 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -7,7 +7,6 @@ import ( "go.uber.org/zap" "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/encryption/multidevice" "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" @@ -49,7 +48,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta } // A new chat must contain us - if !group.IsMember(types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))) { + if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) { return errors.New("can't create a new group chat without us being a member") } newChat := createGroupChat() @@ -198,6 +197,21 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta return nil } +func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) error { + chatID := message.Id + _, ok := state.AllChats[chatID] + if ok { + return nil + } + + chat := CreatePublicChat(chatID) + + state.AllChats[chat.ID] = &chat + state.ModifiedChats[chat.ID] = true + + return nil +} + func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error { logger := m.logger.With(zap.String("site", "HandleContactUpdate")) contact := state.CurrentMessageState.Contact @@ -212,7 +226,7 @@ func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, messag if contact.LastUpdated < message.Clock { logger.Info("Updating contact") - if !contact.HasBeenAdded() { + if !contact.HasBeenAdded() && contact.ID != contactIDFromPublicKey(&m.identity.PublicKey) { contact.SystemTags = append(contact.SystemTags, contactRequestReceived) } if contact.Name != message.EnsName { @@ -333,7 +347,7 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Text: "Request address for transaction", - ChatId: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + ChatId: contactIDFromPublicKey(&m.identity.PublicKey), MessageType: protobuf.ChatMessage_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, @@ -357,7 +371,7 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Text: "Request transaction", - ChatId: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + ChatId: contactIDFromPublicKey(&m.identity.PublicKey), MessageType: protobuf.ChatMessage_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, @@ -563,7 +577,7 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat) case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE: // 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)) + chatID := contactIDFromPublicKey(message.SigPubKey) chat := chats[chatID] if chat == nil { // TODO: this should be a three-word name used in the mobile client @@ -580,8 +594,8 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat) return nil, errors.New("received group chat message for non-existing chat") } - theirKeyHex := types.EncodeHex(crypto.FromECDSAPub(message.SigPubKey)) - myKeyHex := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + theirKeyHex := contactIDFromPublicKey(message.SigPubKey) + myKeyHex := contactIDFromPublicKey(&m.identity.PublicKey) var theyJoined bool var iJoined bool for _, member := range chat.Members { diff --git a/protocol/messenger.go b/protocol/messenger.go index 111b5f58e..30db150dd 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -83,7 +83,7 @@ type MessengerResponse struct { } func (m *MessengerResponse) IsEmpty() bool { - return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.RawMessages) == 0 + return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.RawMessages) == 0 && len(m.Installations) == 0 } type featureFlags struct { @@ -216,7 +216,7 @@ func NewMessenger( onNewInstallationsHandler := func(installations []*multidevice.Installation) { for _, installation := range installations { - if installation.Identity == types.EncodeHex(crypto.FromECDSAPub(&messenger.identity.PublicKey)) { + if installation.Identity == contactIDFromPublicKey(&messenger.identity.PublicKey) { if _, ok := messenger.allInstallations[installation.ID]; !ok { messenger.allInstallations[installation.ID] = installation messenger.modifiedInstallations[installation.ID] = true @@ -595,7 +595,7 @@ func (m *Messenger) Join(chat Chat) error { } return m.transport.JoinGroup(members) case ChatTypePublic: - return m.transport.JoinPublic(chat.Name) + return m.transport.JoinPublic(chat.ID) default: return errors.New("chat is neither public nor private") } @@ -638,8 +638,9 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, } chat.updateChatFromProtocolGroup(group) + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewMembersAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -704,8 +705,9 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Remove member - event := v1protocol.NewMemberRemovedEvent(member, group.NextClockValue()) + event := v1protocol.NewMemberRemovedEvent(member, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -755,8 +757,9 @@ func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, me return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewMembersAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -815,8 +818,9 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewAdminsAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewAdminsAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -877,8 +881,9 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me if err != nil { return nil, err } + clock, _ := chat.NextClockAndTimestamp() event := v1protocol.NewMemberJoinedEvent( - group.NextClockValue(), + clock, ) event.ChatID = chat.ID err = event.Sign(m.identity) @@ -939,9 +944,10 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*Messeng if err != nil { return nil, err } + clock, _ := chat.NextClockAndTimestamp() event := v1protocol.NewMemberRemovedEvent( - types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), - group.NextClockValue(), + contactIDFromPublicKey(&m.identity.PublicKey), + clock, ) event.ChatID = chat.ID err = event.Sign(m.identity) @@ -983,6 +989,14 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*Messeng } func (m *Messenger) saveChat(chat *Chat) error { + _, ok := m.allChats[chat.ID] + // Sync chat if it's a new active public chat + if !ok && chat.Active && chat.Public() { + if err := m.syncPublicChat(context.Background(), chat); err != nil { + return err + } + } + err := m.persistence.SaveChat(*chat) if err != nil { return err @@ -1008,12 +1022,7 @@ func (m *Messenger) saveChats(chats []*Chat) error { 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 + return m.saveChat(chat) } func (m *Messenger) Chats() []*Chat { @@ -1345,6 +1354,13 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag m.mutex.Lock() defer m.mutex.Unlock() + myID := contactIDFromPublicKey(&m.identity.PublicKey) + + if _, err := m.sendContactUpdate(ctx, myID, ensName, profileImage); err != nil { + return err + } + + // TODO: This should not be sending paired messages, as we do it above for _, contact := range m.allContacts { if contact.IsAdded() { if _, err := m.sendContactUpdate(ctx, contact.ID, ensName, profileImage); err != nil { @@ -1353,7 +1369,6 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag } } return nil - } // SendContactUpdate sends a contact update to a user and adds the user to contacts @@ -1417,7 +1432,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof return nil, err } - if !contact.IsAdded() { + if !contact.IsAdded() && contact.ID != contactIDFromPublicKey(&m.identity.PublicKey) { contact.SystemTags = append(contact.SystemTags, contactAdded) } @@ -1432,6 +1447,36 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof return &response, m.saveContact(contact) } +// SyncDevices sends all public chats and contacts to paired devices +func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + myID := contactIDFromPublicKey(&m.identity.PublicKey) + + if _, err := m.sendContactUpdate(ctx, myID, ensName, photoPath); err != nil { + return err + } + + for _, chat := range m.allChats { + if chat.Public() && chat.Active { + if err := m.syncPublicChat(ctx, chat); err != nil { + return err + } + } + } + + for _, contact := range m.allContacts { + if contact.IsAdded() && contact.ID != myID { + if err := m.syncContact(ctx, contact); err != nil { + return err + } + } + } + + return nil +} + // SendPairInstallation sends a pair installation message func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerResponse, error) { m.mutex.Lock() @@ -1449,7 +1494,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons return nil, errors.New("no installation metadata") } - chatID := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + chatID := contactIDFromPublicKey(&m.identity.PublicKey) chat, ok := m.allChats[chatID] if !ok { @@ -1491,13 +1536,54 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons return &response, nil } +// syncPublicChat sync a public chat with paired devices +func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error { + var err error + if !m.hasPairedDevices() { + return nil + } + chatID := contactIDFromPublicKey(&m.identity.PublicKey) + + chat, ok := m.allChats[chatID] + if !ok { + chat = OneToOneFromPublicKey(&m.identity.PublicKey) + // We don't want to show the chat to the user + chat.Active = false + } + + m.allChats[chat.ID] = chat + clock, _ := chat.NextClockAndTimestamp() + + syncMessage := &protobuf.SyncInstallationPublicChat{ + Clock: clock, + Id: publicChat.ID, + } + encodedMessage, err := proto.Marshal(syncMessage) + if err != nil { + return err + } + + _, err = m.dispatchMessage(ctx, &RawMessage{ + LocalChatID: chatID, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT, + ResendAutomatically: true, + }) + if err != nil { + return err + } + + chat.LastClockValue = clock + return m.saveChat(chat) +} + // syncContact sync as contact with paired devices func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { var err error if !m.hasPairedDevices() { return nil } - chatID := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + chatID := contactIDFromPublicKey(&m.identity.PublicKey) chat, ok := m.allChats[chatID] if !ok { @@ -1624,7 +1710,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte publicKey := msg.SigPubKey() // Check for messages from blocked users - senderID := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + senderID := contactIDFromPublicKey(publicKey) if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() { continue } @@ -1706,6 +1792,19 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte logger.Warn("failed to handle SyncInstallationContact", zap.Error(err)) continue } + case protobuf.SyncInstallationPublicChat: + if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + logger.Warn("not coming from us, ignoring") + continue + } + + p := msg.ParsedMessage.(protobuf.SyncInstallationPublicChat) + logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) + err = m.handler.HandleSyncInstallationPublicChat(messageState, p) + if err != nil { + logger.Warn("failed to handle SyncInstallationPublicChat", zap.Error(err)) + continue + } case protobuf.RequestAddressForTransaction: command := msg.ParsedMessage.(protobuf.RequestAddressForTransaction) logger.Debug("Handling RequestAddressForTransaction", zap.Any("message", command)) @@ -2479,7 +2578,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas return &response, m.saveChat(chat) } -func (m *Messenger) SendTransaction(ctx context.Context, chatID, transactionHash string, signature []byte) (*MessengerResponse, error) { +func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature []byte) (*MessengerResponse, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -2534,6 +2633,8 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, transactionHash message.ID = types.EncodeHex(newMessageID) message.CommandParameters = &CommandParameters{ TransactionHash: transactionHash, + Value: value, + Contract: contract, Signature: signature, CommandState: CommandStateTransactionSent, } @@ -2584,7 +2685,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. } for _, validationResult := range responses { var message *Message - chatID := types.EncodeHex(crypto.FromECDSAPub(validationResult.Transaction.From)) + chatID := contactIDFromPublicKey(validationResult.Transaction.From) chat, ok := m.allChats[chatID] if !ok { chat = OneToOneFromPublicKey(validationResult.Transaction.From) diff --git a/protocol/messenger_installations_test.go b/protocol/messenger_installations_test.go index 46e61b3d3..3f113610c 100644 --- a/protocol/messenger_installations_test.go +++ b/protocol/messenger_installations_test.go @@ -141,7 +141,135 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() { actualContact := response.Contacts[0] s.Require().Equal(contact.ID, actualContact.ID) s.Require().True(actualContact.IsAdded()) + + chat := CreatePublicChat("status") + err = s.m.SaveChat(&chat) + s.Require().NoError(err) + + // Wait for the message to reach its destination + err = tt.RetryWithBackOff(func() error { + var err error + response, err = theirMessenger.RetrieveAll() + if err == nil && len(response.Chats) == 0 { + err = errors.New("sync chat not received") + } + return err + }) + s.Require().NoError(err) + + actualChat := response.Chats[0] + s.Require().Equal("status", actualChat.ID) + s.Require().True(actualChat.Active) } -func (s *MessengerInstallationSuite) TestReceiveSyncInstallation() { +func (s *MessengerInstallationSuite) TestSyncInstallation() { + + // add contact + contactKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + contact, err := buildContact(&contactKey.PublicKey) + s.Require().NoError(err) + contact.SystemTags = append(contact.SystemTags, contactAdded) + err = s.m.SaveContact(contact) + s.Require().NoError(err) + + // add chat + chat := CreatePublicChat("status") + err = s.m.SaveChat(&chat) + s.Require().NoError(err) + + // pair + theirMessenger := s.newMessengerWithKey(s.shh, s.privateKey) + + err = theirMessenger.SetInstallationMetadata(theirMessenger.installationID, &multidevice.InstallationMetadata{ + Name: "their-name", + DeviceType: "their-device-type", + }) + s.Require().NoError(err) + response, err := theirMessenger.SendPairInstallation(context.Background()) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.Chats, 1) + s.Require().False(response.Chats[0].Active) + + // Wait for the message to reach its destination + err = tt.RetryWithBackOff(func() error { + var err error + response, err = s.m.RetrieveAll() + if err == nil && len(response.Installations) == 0 { + err = errors.New("installation not received") + } + return err + }) + s.Require().NoError(err) + actualInstallation := response.Installations[0] + s.Require().Equal(theirMessenger.installationID, actualInstallation.ID) + s.Require().NotNil(actualInstallation.InstallationMetadata) + s.Require().Equal("their-name", actualInstallation.InstallationMetadata.Name) + s.Require().Equal("their-device-type", actualInstallation.InstallationMetadata.DeviceType) + + err = s.m.EnableInstallation(theirMessenger.installationID) + s.Require().NoError(err) + + // sync + err = s.m.SyncDevices(context.Background(), "ens-name", "profile-image") + s.Require().NoError(err) + + var allChats []*Chat + var allContacts []*Contact + // Wait for the message to reach its destination + err = tt.RetryWithBackOff(func() error { + var err error + response, err = theirMessenger.RetrieveAll() + if err != nil { + return err + } + + allChats = append(allChats, response.Chats...) + allContacts = append(allContacts, response.Contacts...) + + if len(allChats) >= 2 && len(allContacts) >= 3 { + return nil + } + + return errors.New("Not received all chats & contacts") + + }) + + s.Require().NoError(err) + + var statusChat *Chat + for _, c := range allChats { + if c.ID == "status" { + statusChat = c + } + } + + s.Require().NotNil(statusChat) + + var actualContact *Contact + for _, c := range allContacts { + if c.ID == contact.ID { + actualContact = c + } + } + + s.Require().True(actualContact.IsAdded()) + + var ourContact *Contact + + myID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey)) + for _, c := range allContacts { + if c.ID == myID { + if ourContact == nil || ourContact.LastUpdated < c.LastUpdated { + ourContact = c + } + } + } + + s.Require().NotNil(ourContact) + s.Require().Equal("ens-name", ourContact.Name) + s.Require().Equal("profile-image", ourContact.Photo) + } diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index df72fdca4..7f7fc01d5 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -1014,6 +1014,19 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() { } func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { + + member1Key, err := crypto.GenerateKey() + s.Require().NoError(err) + member1ID := types.EncodeHex(crypto.FromECDSAPub(&member1Key.PublicKey)) + + member2Key, err := crypto.GenerateKey() + s.Require().NoError(err) + member2ID := types.EncodeHex(crypto.FromECDSAPub(&member2Key.PublicKey)) + + member3Key, err := crypto.GenerateKey() + s.Require().NoError(err) + member3ID := types.EncodeHex(crypto.FromECDSAPub(&member3Key.PublicKey)) + chat := Chat{ ID: "chat-id", Name: "chat-id", @@ -1023,17 +1036,17 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { Timestamp: 10, Members: []ChatMember{ { - ID: "1", + ID: member1ID, Admin: false, Joined: true, }, { - ID: "2", + ID: member2ID, Admin: true, Joined: false, }, { - ID: "3", + ID: member3ID, Admin: true, Joined: true, }, @@ -1554,6 +1567,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() { func (s *MessengerSuite) TestSendEthTransaction() { value := "2000" + contract := "some-contract" theirMessenger := s.newMessenger(s.shh) theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey)) @@ -1569,7 +1583,7 @@ func (s *MessengerSuite) TestSendEthTransaction() { signature, err := buildSignature(s.m.identity, &s.m.identity.PublicKey, transactionHash) s.Require().NoError(err) - response, err := s.m.SendTransaction(context.Background(), theirPkString, transactionHash, signature) + response, err := s.m.SendTransaction(context.Background(), theirPkString, value, contract, transactionHash, signature) s.Require().NoError(err) s.Require().NotNil(response) s.Require().Len(response.Chats, 1) @@ -1580,6 +1594,8 @@ func (s *MessengerSuite) TestSendEthTransaction() { s.Require().Equal("Transaction sent", senderMessage.Text) s.Require().NotNil(senderMessage.CommandParameters) s.Require().Equal(transactionHash, senderMessage.CommandParameters.TransactionHash) + s.Require().Equal(contract, senderMessage.CommandParameters.Contract) + s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(signature, senderMessage.CommandParameters.Signature) s.Require().Equal(CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) s.Require().NotEmpty(senderMessage.ID) @@ -1667,7 +1683,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() { signature, err := buildSignature(s.m.identity, &s.m.identity.PublicKey, transactionHash) s.Require().NoError(err) - response, err := s.m.SendTransaction(context.Background(), theirPkString, transactionHash, signature) + response, err := s.m.SendTransaction(context.Background(), theirPkString, value, contract, transactionHash, signature) s.Require().NoError(err) s.Require().NotNil(response) s.Require().Len(response.Chats, 1) @@ -1678,6 +1694,8 @@ func (s *MessengerSuite) TestSendTokenTransaction() { s.Require().Equal("Transaction sent", senderMessage.Text) s.Require().NotNil(senderMessage.CommandParameters) s.Require().Equal(transactionHash, senderMessage.CommandParameters.TransactionHash) + s.Require().Equal(value, senderMessage.CommandParameters.Value) + s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(signature, senderMessage.CommandParameters.Signature) s.Require().Equal(CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) s.Require().NotEmpty(senderMessage.ID) diff --git a/protocol/v1/membership_update_message.go b/protocol/v1/membership_update_message.go index 2e1bed012..a2d42430d 100644 --- a/protocol/v1/membership_update_message.go +++ b/protocol/v1/membership_update_message.go @@ -342,10 +342,6 @@ func (g Group) LastClockValue() uint64 { return g.events[len(g.events)-1].ClockValue } -func (g Group) NextClockValue() uint64 { - return g.LastClockValue() + 1 -} - func (g Group) creator() (string, error) { if len(g.events) == 0 { return "", errors.New("no events in the group") diff --git a/services/shhext/api.go b/services/shhext/api.go index 9522a546a..84fb75496 100644 --- a/services/shhext/api.go +++ b/services/shhext/api.go @@ -721,8 +721,8 @@ func (api *PublicAPI) AcceptRequestAddressForTransaction(ctx context.Context, me return api.service.messenger.AcceptRequestAddressForTransaction(ctx, messageID, address) } -func (api *PublicAPI) SendTransaction(ctx context.Context, chatID, transactionHash string, signature types.HexBytes) (*protocol.MessengerResponse, error) { - return api.service.messenger.SendTransaction(ctx, chatID, transactionHash, signature) +func (api *PublicAPI) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature types.HexBytes) (*protocol.MessengerResponse, error) { + return api.service.messenger.SendTransaction(ctx, chatID, value, contract, transactionHash, signature) } func (api *PublicAPI) AcceptRequestTransaction(ctx context.Context, transactionHash, messageID string, signature types.HexBytes) (*protocol.MessengerResponse, error) { @@ -737,6 +737,14 @@ func (api *PublicAPI) SendContactUpdate(ctx context.Context, contactID, name, pi return api.service.messenger.SendContactUpdate(ctx, contactID, name, picture) } +func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.MessengerResponse, error) { + return api.service.messenger.SendPairInstallation(ctx) +} + +func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error { + return api.service.messenger.SyncDevices(ctx, name, picture) +} + // ----- // HELPER // ----- 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 74600b64d..08988daca 100644 --- a/vendor/github.com/status-im/status-go/protocol/chat.go +++ b/vendor/github.com/status-im/status-go/protocol/chat.go @@ -75,6 +75,10 @@ func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) { } +func (c *Chat) Public() bool { + return c.ChatType == ChatTypePublic +} + func (c *Chat) MarshalJSON() ([]byte, error) { type ChatAlias Chat item := struct { @@ -212,20 +216,22 @@ func OneToOneFromPublicKey(pk *ecdsa.PublicKey) *Chat { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey) Chat { return Chat{ - ID: oneToOneChatID(publicKey), - Name: name, - Active: true, - ChatType: ChatTypeOneToOne, + ID: oneToOneChatID(publicKey), + Name: name, + Timestamp: int64(timestampInMs()), + Active: true, + ChatType: ChatTypeOneToOne, } } func CreatePublicChat(name string) Chat { return Chat{ - ID: name, - Name: name, - Active: true, - Color: chatColors[rand.Intn(len(chatColors))], - ChatType: ChatTypePublic, + ID: name, + Name: name, + Active: true, + Timestamp: int64(timestampInMs()), + Color: chatColors[rand.Intn(len(chatColors))], + ChatType: ChatTypePublic, } } diff --git a/vendor/github.com/status-im/status-go/protocol/contact.go b/vendor/github.com/status-im/status-go/protocol/contact.go index cc6c2769f..6f42f6be6 100644 --- a/vendor/github.com/status-im/status-go/protocol/contact.go +++ b/vendor/github.com/status-im/status-go/protocol/contact.go @@ -106,3 +106,8 @@ func buildContact(publicKey *ecdsa.PublicKey) (*Contact, error) { return contact, nil } + +func contactIDFromPublicKey(key *ecdsa.PublicKey) string { + return types.EncodeHex(crypto.FromECDSAPub(key)) + +} 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 9c69bc4e1..a9010509b 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 v0.0.0-20191104174740-4d42851d4d5a => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba +replace github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba replace github.com/status-im/status-go/eth-node => ../eth-node @@ -14,7 +14,7 @@ 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-20191104174740-4d42851d4d5a + 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 @@ -23,9 +23,8 @@ require ( 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 v0.38.5 // indirect - github.com/status-im/status-go/eth-node v1.0.0 - github.com/status-im/status-go/whisper/v6 v6.0.1 + github.com/status-im/status-go/eth-node v1.1.0 + github.com/status-im/status-go/whisper/v6 v6.1.0 github.com/stretchr/testify v1.4.0 github.com/vacp2p/mvds v0.0.23 go.uber.org/zap v1.13.0 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 050eb91f7..7620481b2 100644 --- a/vendor/github.com/status-im/status-go/protocol/go.sum +++ b/vendor/github.com/status-im/status-go/protocol/go.sum @@ -156,9 +156,11 @@ github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zac 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= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= 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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -196,6 +198,7 @@ github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a h1:DsPLKbIJTzH github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -289,6 +292,7 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= @@ -763,6 +767,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191213032237-7093a17b0467 h1:Jybbe55FT+YYZIJGWmJIA4ZGcglFuZOduakIW3+gHXY= golang.org/x/tools v0.0.0-20191213032237-7093a17b0467/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= diff --git a/vendor/github.com/status-im/status-go/protocol/message.go b/vendor/github.com/status-im/status-go/protocol/message.go index 279833639..a58610a6a 100644 --- a/vendor/github.com/status-im/status-go/protocol/message.go +++ b/vendor/github.com/status-im/status-go/protocol/message.go @@ -113,6 +113,10 @@ type RawMessage struct { func (m *Message) MarshalJSON() ([]byte, error) { type MessageAlias Message + type StickerAlias struct { + Hash string `json:"hash"` + Pack int32 `json:"pack"` + } item := struct { ID string `json:"id"` WhisperTimestamp uint64 `json:"whisperTimestamp"` @@ -132,7 +136,7 @@ func (m *Message) MarshalJSON() ([]byte, error) { Replace string `json:"replace,omitEmpty"` ResponseTo string `json:"responseTo"` EnsName string `json:"ensName"` - Sticker *protobuf.StickerMessage `json:"sticker"` + Sticker *StickerAlias `json:"sticker"` CommandParameters *CommandParameters `json:"commandParameters"` Timestamp uint64 `json:"timestamp"` ContentType protobuf.ChatMessage_ContentType `json:"contentType"` @@ -159,10 +163,15 @@ func (m *Message) MarshalJSON() ([]byte, error) { Timestamp: m.Timestamp, ContentType: m.ContentType, MessageType: m.MessageType, - Sticker: m.GetSticker(), CommandParameters: m.CommandParameters, } + if sticker := m.GetSticker(); sticker != nil { + item.Sticker = &StickerAlias{ + Pack: sticker.Pack, + Hash: sticker.Hash, + } + } return json.Marshal(item) } 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 bbcfcd202..d051f22e3 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 @@ -7,7 +7,6 @@ import ( "go.uber.org/zap" "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/encryption/multidevice" "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" @@ -49,7 +48,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta } // A new chat must contain us - if !group.IsMember(types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))) { + if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) { return errors.New("can't create a new group chat without us being a member") } newChat := createGroupChat() @@ -198,6 +197,21 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta return nil } +func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) error { + chatID := message.Id + _, ok := state.AllChats[chatID] + if ok { + return nil + } + + chat := CreatePublicChat(chatID) + + state.AllChats[chat.ID] = &chat + state.ModifiedChats[chat.ID] = true + + return nil +} + func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error { logger := m.logger.With(zap.String("site", "HandleContactUpdate")) contact := state.CurrentMessageState.Contact @@ -212,7 +226,7 @@ func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, messag if contact.LastUpdated < message.Clock { logger.Info("Updating contact") - if !contact.HasBeenAdded() { + if !contact.HasBeenAdded() && contact.ID != contactIDFromPublicKey(&m.identity.PublicKey) { contact.SystemTags = append(contact.SystemTags, contactRequestReceived) } if contact.Name != message.EnsName { @@ -333,7 +347,7 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Text: "Request address for transaction", - ChatId: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + ChatId: contactIDFromPublicKey(&m.identity.PublicKey), MessageType: protobuf.ChatMessage_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, @@ -357,7 +371,7 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Text: "Request transaction", - ChatId: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), + ChatId: contactIDFromPublicKey(&m.identity.PublicKey), MessageType: protobuf.ChatMessage_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, @@ -563,7 +577,7 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat) case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE: // 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)) + chatID := contactIDFromPublicKey(message.SigPubKey) chat := chats[chatID] if chat == nil { // TODO: this should be a three-word name used in the mobile client @@ -580,8 +594,8 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat) return nil, errors.New("received group chat message for non-existing chat") } - theirKeyHex := types.EncodeHex(crypto.FromECDSAPub(message.SigPubKey)) - myKeyHex := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + theirKeyHex := contactIDFromPublicKey(message.SigPubKey) + myKeyHex := contactIDFromPublicKey(&m.identity.PublicKey) var theyJoined bool var iJoined bool for _, member := range chat.Members { 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 111b5f58e..30db150dd 100644 --- a/vendor/github.com/status-im/status-go/protocol/messenger.go +++ b/vendor/github.com/status-im/status-go/protocol/messenger.go @@ -83,7 +83,7 @@ type MessengerResponse struct { } func (m *MessengerResponse) IsEmpty() bool { - return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.RawMessages) == 0 + return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.RawMessages) == 0 && len(m.Installations) == 0 } type featureFlags struct { @@ -216,7 +216,7 @@ func NewMessenger( onNewInstallationsHandler := func(installations []*multidevice.Installation) { for _, installation := range installations { - if installation.Identity == types.EncodeHex(crypto.FromECDSAPub(&messenger.identity.PublicKey)) { + if installation.Identity == contactIDFromPublicKey(&messenger.identity.PublicKey) { if _, ok := messenger.allInstallations[installation.ID]; !ok { messenger.allInstallations[installation.ID] = installation messenger.modifiedInstallations[installation.ID] = true @@ -595,7 +595,7 @@ func (m *Messenger) Join(chat Chat) error { } return m.transport.JoinGroup(members) case ChatTypePublic: - return m.transport.JoinPublic(chat.Name) + return m.transport.JoinPublic(chat.ID) default: return errors.New("chat is neither public nor private") } @@ -638,8 +638,9 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, } chat.updateChatFromProtocolGroup(group) + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewMembersAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -704,8 +705,9 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Remove member - event := v1protocol.NewMemberRemovedEvent(member, group.NextClockValue()) + event := v1protocol.NewMemberRemovedEvent(member, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -755,8 +757,9 @@ func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, me return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewMembersAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewMembersAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -815,8 +818,9 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem return nil, err } + clock, _ := chat.NextClockAndTimestamp() // Add members - event := v1protocol.NewAdminsAddedEvent(members, group.NextClockValue()) + event := v1protocol.NewAdminsAddedEvent(members, clock) event.ChatID = chat.ID err = event.Sign(m.identity) if err != nil { @@ -877,8 +881,9 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me if err != nil { return nil, err } + clock, _ := chat.NextClockAndTimestamp() event := v1protocol.NewMemberJoinedEvent( - group.NextClockValue(), + clock, ) event.ChatID = chat.ID err = event.Sign(m.identity) @@ -939,9 +944,10 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*Messeng if err != nil { return nil, err } + clock, _ := chat.NextClockAndTimestamp() event := v1protocol.NewMemberRemovedEvent( - types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)), - group.NextClockValue(), + contactIDFromPublicKey(&m.identity.PublicKey), + clock, ) event.ChatID = chat.ID err = event.Sign(m.identity) @@ -983,6 +989,14 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string) (*Messeng } func (m *Messenger) saveChat(chat *Chat) error { + _, ok := m.allChats[chat.ID] + // Sync chat if it's a new active public chat + if !ok && chat.Active && chat.Public() { + if err := m.syncPublicChat(context.Background(), chat); err != nil { + return err + } + } + err := m.persistence.SaveChat(*chat) if err != nil { return err @@ -1008,12 +1022,7 @@ func (m *Messenger) saveChats(chats []*Chat) error { 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 + return m.saveChat(chat) } func (m *Messenger) Chats() []*Chat { @@ -1345,6 +1354,13 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag m.mutex.Lock() defer m.mutex.Unlock() + myID := contactIDFromPublicKey(&m.identity.PublicKey) + + if _, err := m.sendContactUpdate(ctx, myID, ensName, profileImage); err != nil { + return err + } + + // TODO: This should not be sending paired messages, as we do it above for _, contact := range m.allContacts { if contact.IsAdded() { if _, err := m.sendContactUpdate(ctx, contact.ID, ensName, profileImage); err != nil { @@ -1353,7 +1369,6 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag } } return nil - } // SendContactUpdate sends a contact update to a user and adds the user to contacts @@ -1417,7 +1432,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof return nil, err } - if !contact.IsAdded() { + if !contact.IsAdded() && contact.ID != contactIDFromPublicKey(&m.identity.PublicKey) { contact.SystemTags = append(contact.SystemTags, contactAdded) } @@ -1432,6 +1447,36 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof return &response, m.saveContact(contact) } +// SyncDevices sends all public chats and contacts to paired devices +func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + + myID := contactIDFromPublicKey(&m.identity.PublicKey) + + if _, err := m.sendContactUpdate(ctx, myID, ensName, photoPath); err != nil { + return err + } + + for _, chat := range m.allChats { + if chat.Public() && chat.Active { + if err := m.syncPublicChat(ctx, chat); err != nil { + return err + } + } + } + + for _, contact := range m.allContacts { + if contact.IsAdded() && contact.ID != myID { + if err := m.syncContact(ctx, contact); err != nil { + return err + } + } + } + + return nil +} + // SendPairInstallation sends a pair installation message func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerResponse, error) { m.mutex.Lock() @@ -1449,7 +1494,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons return nil, errors.New("no installation metadata") } - chatID := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + chatID := contactIDFromPublicKey(&m.identity.PublicKey) chat, ok := m.allChats[chatID] if !ok { @@ -1491,13 +1536,54 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons return &response, nil } +// syncPublicChat sync a public chat with paired devices +func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error { + var err error + if !m.hasPairedDevices() { + return nil + } + chatID := contactIDFromPublicKey(&m.identity.PublicKey) + + chat, ok := m.allChats[chatID] + if !ok { + chat = OneToOneFromPublicKey(&m.identity.PublicKey) + // We don't want to show the chat to the user + chat.Active = false + } + + m.allChats[chat.ID] = chat + clock, _ := chat.NextClockAndTimestamp() + + syncMessage := &protobuf.SyncInstallationPublicChat{ + Clock: clock, + Id: publicChat.ID, + } + encodedMessage, err := proto.Marshal(syncMessage) + if err != nil { + return err + } + + _, err = m.dispatchMessage(ctx, &RawMessage{ + LocalChatID: chatID, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT, + ResendAutomatically: true, + }) + if err != nil { + return err + } + + chat.LastClockValue = clock + return m.saveChat(chat) +} + // syncContact sync as contact with paired devices func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { var err error if !m.hasPairedDevices() { return nil } - chatID := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)) + chatID := contactIDFromPublicKey(&m.identity.PublicKey) chat, ok := m.allChats[chatID] if !ok { @@ -1624,7 +1710,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte publicKey := msg.SigPubKey() // Check for messages from blocked users - senderID := types.EncodeHex(crypto.FromECDSAPub(publicKey)) + senderID := contactIDFromPublicKey(publicKey) if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() { continue } @@ -1706,6 +1792,19 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte logger.Warn("failed to handle SyncInstallationContact", zap.Error(err)) continue } + case protobuf.SyncInstallationPublicChat: + if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + logger.Warn("not coming from us, ignoring") + continue + } + + p := msg.ParsedMessage.(protobuf.SyncInstallationPublicChat) + logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) + err = m.handler.HandleSyncInstallationPublicChat(messageState, p) + if err != nil { + logger.Warn("failed to handle SyncInstallationPublicChat", zap.Error(err)) + continue + } case protobuf.RequestAddressForTransaction: command := msg.ParsedMessage.(protobuf.RequestAddressForTransaction) logger.Debug("Handling RequestAddressForTransaction", zap.Any("message", command)) @@ -2479,7 +2578,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas return &response, m.saveChat(chat) } -func (m *Messenger) SendTransaction(ctx context.Context, chatID, transactionHash string, signature []byte) (*MessengerResponse, error) { +func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature []byte) (*MessengerResponse, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -2534,6 +2633,8 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, transactionHash message.ID = types.EncodeHex(newMessageID) message.CommandParameters = &CommandParameters{ TransactionHash: transactionHash, + Value: value, + Contract: contract, Signature: signature, CommandState: CommandStateTransactionSent, } @@ -2584,7 +2685,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. } for _, validationResult := range responses { var message *Message - chatID := types.EncodeHex(crypto.FromECDSAPub(validationResult.Transaction.From)) + chatID := contactIDFromPublicKey(validationResult.Transaction.From) chat, ok := m.allChats[chatID] if !ok { chat = OneToOneFromPublicKey(validationResult.Transaction.From) 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 2e1bed012..a2d42430d 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 @@ -342,10 +342,6 @@ func (g Group) LastClockValue() uint64 { return g.events[len(g.events)-1].ClockValue } -func (g Group) NextClockValue() uint64 { - return g.LastClockValue() + 1 -} - func (g Group) creator() (string, error) { if len(g.events) == 0 { return "", errors.New("no events in the group") diff --git a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go index e740c8f02..813385a98 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go @@ -53,6 +53,7 @@ type sockaddrInet6 struct { const ( sizeofIovec = 0x10 sizeofMsghdr = 0x30 + sizeofMmsghdr = 0x38 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go index d33025b70..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go @@ -47,6 +47,7 @@ type sockaddrInet6 struct { const ( sizeofIovec = 0x8 sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go index b20d21677..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go @@ -50,6 +50,7 @@ type sockaddrInet6 struct { const ( sizeofIovec = 0x10 sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go index 1bb10a428..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go @@ -45,9 +45,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c - + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go index 1bb10a428..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go @@ -45,9 +45,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c - + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go index 1bb10a428..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go @@ -45,9 +45,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c - + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go index f12a1d768..dbff234fb 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go @@ -49,9 +49,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go index 7f6e8a7fa..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go @@ -48,9 +48,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x38 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0x10 sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go index 7e258cec2..bf8f47c88 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go @@ -47,9 +47,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c - + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go index b3f9c0d7e..a46eff991 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go @@ -50,9 +50,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x10 - sizeofMsghdr = 0x30 - + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go index 7e258cec2..bf8f47c88 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go @@ -47,9 +47,9 @@ type sockaddrInet6 struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c - + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go index da26ef019..a46eff991 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go @@ -52,6 +52,7 @@ type sockaddrInet6 struct { const ( sizeofIovec = 0x10 sizeofMsghdr = 0x30 + sizeofMmsghdr = 0x40 sizeofCmsghdr = 0xc sizeofSockaddrInet = 0x10 diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index 4bfe28a51..3799f8ed8 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -60,7 +60,8 @@ causes Load to run in LoadFiles mode, collecting minimal information. See the documentation for type Config for details. As noted earlier, the Config.Mode controls the amount of detail -reported about the loaded packages. See the documentation for type LoadMode +reported about the loaded packages, with each mode returning all the data of the +previous mode with some extra added. See the documentation for type LoadMode for details. Most tools should pass their command-line arguments (after any flags) diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index 8c8473fd0..6ac3e4f5b 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -84,14 +84,13 @@ func findExternalDriver(cfg *Config) driver { cmd.Stdin = bytes.NewReader(req) cmd.Stdout = buf cmd.Stderr = stderr - - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) - } if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, words...), stderr) } + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) + } var response driverResponse if err := json.Unmarshal(buf.Bytes(), &response); err != nil { return nil, err diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 9c895b389..c581bce97 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -26,6 +26,7 @@ import ( "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/semver" + "golang.org/x/tools/internal/span" ) // debug controls verbose logging. @@ -253,7 +254,12 @@ func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDedu if len(pkgs) == 0 { return nil } - dr, err := driver(cfg, pkgs...) + drivercfg := *cfg + if getGoInfo().env.modulesOn { + drivercfg.BuildFlags = append(drivercfg.BuildFlags, "-mod=readonly") + } + dr, err := driver(&drivercfg, pkgs...) + if err != nil { return err } @@ -264,7 +270,10 @@ func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDedu if err != nil { return err } - return addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo) + if err := addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo); err != nil { + return err + } + return nil } func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, goInfo func() *goInfo) error { @@ -278,43 +287,42 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) } dirResponse, err := driver(cfg, pattern) - if err != nil || (len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1) { - // There was an error loading the package. Try to load the file as an ad-hoc package. - // Usually the error will appear in a returned package, but may not if we're in modules mode - // and the ad-hoc is located outside a module. + if err != nil { var queryErr error - dirResponse, queryErr = driver(cfg, query) - if queryErr != nil { - // Return the original error if the attempt to fall back failed. - return err + if dirResponse, queryErr = adHocPackage(cfg, driver, pattern, query); queryErr != nil { + return err // return the original error } - // If we get nothing back from `go list`, try to make this file into its own ad-hoc package. - if len(dirResponse.Packages) == 0 && queryErr == nil { - dirResponse.Packages = append(dirResponse.Packages, &Package{ - ID: "command-line-arguments", - PkgPath: query, - GoFiles: []string{query}, - CompiledGoFiles: []string{query}, - Imports: make(map[string]*Package), - }) - dirResponse.Roots = append(dirResponse.Roots, "command-line-arguments") - } - // Special case to handle issue #33482: - // If this is a file= query for ad-hoc packages where the file only exists on an overlay, - // and exists outside of a module, add the file in for the package. - if len(dirResponse.Packages) == 1 && (dirResponse.Packages[0].ID == "command-line-arguments" || - filepath.ToSlash(dirResponse.Packages[0].PkgPath) == filepath.ToSlash(query)) { - if len(dirResponse.Packages[0].GoFiles) == 0 { - filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath - // TODO(matloob): check if the file is outside of a root dir? - for path := range cfg.Overlay { - if path == filename { - dirResponse.Packages[0].Errors = nil - dirResponse.Packages[0].GoFiles = []string{path} - dirResponse.Packages[0].CompiledGoFiles = []string{path} - } - } + } + // `go list` can report errors for files that are not listed as part of a package's GoFiles. + // In the case of an invalid Go file, we should assume that it is part of package if only + // one package is in the response. The file may have valid contents in an overlay. + if len(dirResponse.Packages) == 1 { + pkg := dirResponse.Packages[0] + for i, err := range pkg.Errors { + s := errorSpan(err) + if !s.IsValid() { + break } + if len(pkg.CompiledGoFiles) == 0 { + break + } + dir := filepath.Dir(pkg.CompiledGoFiles[0]) + filename := filepath.Join(dir, filepath.Base(s.URI().Filename())) + if info, err := os.Stat(filename); err != nil || info.IsDir() { + break + } + if !contains(pkg.CompiledGoFiles, filename) { + pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename) + pkg.GoFiles = append(pkg.GoFiles, filename) + pkg.Errors = append(pkg.Errors[:i], pkg.Errors[i+1:]...) + } + } + } + // A final attempt to construct an ad-hoc package. + if len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1 { + var queryErr error + if dirResponse, queryErr = adHocPackage(cfg, driver, pattern, query); queryErr != nil { + return err // return the original error } } isRoot := make(map[string]bool, len(dirResponse.Roots)) @@ -342,6 +350,74 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q return nil } +// adHocPackage attempts to construct an ad-hoc package given a query that failed. +func adHocPackage(cfg *Config, driver driver, pattern, query string) (*driverResponse, error) { + // There was an error loading the package. Try to load the file as an ad-hoc package. + // Usually the error will appear in a returned package, but may not if we're in modules mode + // and the ad-hoc is located outside a module. + dirResponse, err := driver(cfg, query) + if err != nil { + return nil, err + } + // If we get nothing back from `go list`, try to make this file into its own ad-hoc package. + if len(dirResponse.Packages) == 0 && err == nil { + dirResponse.Packages = append(dirResponse.Packages, &Package{ + ID: "command-line-arguments", + PkgPath: query, + GoFiles: []string{query}, + CompiledGoFiles: []string{query}, + Imports: make(map[string]*Package), + }) + dirResponse.Roots = append(dirResponse.Roots, "command-line-arguments") + } + // Special case to handle issue #33482: + // If this is a file= query for ad-hoc packages where the file only exists on an overlay, + // and exists outside of a module, add the file in for the package. + if len(dirResponse.Packages) == 1 && (dirResponse.Packages[0].ID == "command-line-arguments" || dirResponse.Packages[0].PkgPath == filepath.ToSlash(query)) { + if len(dirResponse.Packages[0].GoFiles) == 0 { + filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath + // TODO(matloob): check if the file is outside of a root dir? + for path := range cfg.Overlay { + if path == filename { + dirResponse.Packages[0].Errors = nil + dirResponse.Packages[0].GoFiles = []string{path} + dirResponse.Packages[0].CompiledGoFiles = []string{path} + } + } + } + } + return dirResponse, nil +} + +func contains(files []string, filename string) bool { + for _, f := range files { + if f == filename { + return true + } + } + return false +} + +// errorSpan attempts to parse a standard `go list` error message +// by stripping off the trailing error message. +// +// It works only on errors whose message is prefixed by colon, +// followed by a space (": "). For example: +// +// attributes.go:13:1: expected 'package', found 'type' +// +func errorSpan(err Error) span.Span { + if err.Pos == "" { + input := strings.TrimSpace(err.Msg) + msgIndex := strings.Index(input, ": ") + if msgIndex < 0 { + return span.Parse(input) + } + return span.Parse(input[:msgIndex]) + } + return span.Parse(err.Pos) +} + // modCacheRegexp splits a path in a module cache into module, module version, and package. var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) @@ -805,14 +881,9 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv } if p.Error != nil { - msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363. - // Address golang.org/issue/35964 by appending import stack to error message. - if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 { - msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack) - } pkg.Errors = append(pkg.Errors, Error{ Pos: p.Error.Pos, - Msg: msg, + Msg: strings.TrimSpace(p.Error.Err), // Trim to work around golang.org/issue/32363. }) } diff --git a/vendor/golang.org/x/tools/go/packages/loadmode_string.go b/vendor/golang.org/x/tools/go/packages/loadmode_string.go deleted file mode 100644 index aff94a3fe..000000000 --- a/vendor/golang.org/x/tools/go/packages/loadmode_string.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 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 packages - -import ( - "fmt" - "strings" -) - -var allModes = []LoadMode{ - NeedName, - NeedFiles, - NeedCompiledGoFiles, - NeedImports, - NeedDeps, - NeedExportsFile, - NeedTypes, - NeedSyntax, - NeedTypesInfo, - NeedTypesSizes, -} - -var modeStrings = []string{ - "NeedName", - "NeedFiles", - "NeedCompiledGoFiles", - "NeedImports", - "NeedDeps", - "NeedExportsFile", - "NeedTypes", - "NeedSyntax", - "NeedTypesInfo", - "NeedTypesSizes", -} - -func (mod LoadMode) String() string { - m := mod - if m == 0 { - return fmt.Sprintf("LoadMode(0)") - } - var out []string - for i, x := range allModes { - if x > m { - break - } - if (m & x) != 0 { - out = append(out, modeStrings[i]) - m = m ^ x - } - } - if m != 0 { - out = append(out, "Unknown") - } - return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|")) -} diff --git a/vendor/golang.org/x/tools/internal/span/parse.go b/vendor/golang.org/x/tools/internal/span/parse.go new file mode 100644 index 000000000..b3f268a38 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/parse.go @@ -0,0 +1,100 @@ +// Copyright 2019 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 span + +import ( + "strconv" + "strings" + "unicode/utf8" +) + +// Parse returns the location represented by the input. +// All inputs are valid locations, as they can always be a pure filename. +// The returned span will be normalized, and thus if printed may produce a +// different string. +func Parse(input string) Span { + // :0:0#0-0:0#0 + valid := input + var hold, offset int + hadCol := false + suf := rstripSuffix(input) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep == ":" { + valid = suf.remains + hold = suf.num + hadCol = true + suf = rstripSuffix(suf.remains) + } + switch { + case suf.sep == ":": + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) + case suf.sep == "-": + // we have a span, fall out of the case to continue + default: + // separator not valid, rewind to either the : or the start + return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) + } + // only the span form can get here + // at this point we still don't know what the numbers we have mean + // if have not yet seen a : then we might have either a line or a column depending + // on whether start has a column or not + // we build an end point and will fix it later if needed + end := NewPoint(suf.num, hold, offset) + hold, offset = 0, 0 + suf = rstripSuffix(suf.remains) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep != ":" { + // turns out we don't have a span after all, rewind + return New(NewURI(valid), end, Point{}) + } + valid = suf.remains + hold = suf.num + suf = rstripSuffix(suf.remains) + if suf.sep != ":" { + // line#offset only + return New(NewURI(valid), NewPoint(hold, 0, offset), end) + } + // we have a column, so if end only had one number, it is also the column + if !hadCol { + end = NewPoint(suf.num, end.v.Line, end.v.Offset) + } + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) +} + +type suffix struct { + remains string + sep string + num int +} + +func rstripSuffix(input string) suffix { + if len(input) == 0 { + return suffix{"", "", -1} + } + remains := input + num := -1 + // first see if we have a number at the end + last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) + if last >= 0 && last < len(remains)-1 { + number, err := strconv.ParseInt(remains[last+1:], 10, 64) + if err == nil { + num = int(number) + remains = remains[:last+1] + } + } + // now see if we have a trailing separator + r, w := utf8.DecodeLastRuneInString(remains) + if r != ':' && r != '#' && r == '#' { + return suffix{input, "", -1} + } + remains = remains[:len(remains)-w] + return suffix{remains, string(r), num} +} diff --git a/vendor/golang.org/x/tools/internal/span/span.go b/vendor/golang.org/x/tools/internal/span/span.go new file mode 100644 index 000000000..4d2ad0986 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/span.go @@ -0,0 +1,285 @@ +// Copyright 2019 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 span contains support for representing with positions and ranges in +// text files. +package span + +import ( + "encoding/json" + "fmt" + "path" +) + +// Span represents a source code range in standardized form. +type Span struct { + v span +} + +// Point represents a single point within a file. +// In general this should only be used as part of a Span, as on its own it +// does not carry enough information. +type Point struct { + v point +} + +type span struct { + URI URI `json:"uri"` + Start point `json:"start"` + End point `json:"end"` +} + +type point struct { + Line int `json:"line"` + Column int `json:"column"` + Offset int `json:"offset"` +} + +// Invalid is a span that reports false from IsValid +var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} + +var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} + +// Converter is the interface to an object that can convert between line:column +// and offset forms for a single file. +type Converter interface { + //ToPosition converts from an offset to a line:column pair. + ToPosition(offset int) (int, int, error) + //ToOffset converts from a line:column pair to an offset. + ToOffset(line, col int) (int, error) +} + +func New(uri URI, start Point, end Point) Span { + s := Span{v: span{URI: uri, Start: start.v, End: end.v}} + s.v.clean() + return s +} + +func NewPoint(line, col, offset int) Point { + p := Point{v: point{Line: line, Column: col, Offset: offset}} + p.v.clean() + return p +} + +func Compare(a, b Span) int { + if r := CompareURI(a.URI(), b.URI()); r != 0 { + return r + } + if r := comparePoint(a.v.Start, b.v.Start); r != 0 { + return r + } + return comparePoint(a.v.End, b.v.End) +} + +func ComparePoint(a, b Point) int { + return comparePoint(a.v, b.v) +} + +func comparePoint(a, b point) int { + if !a.hasPosition() { + if a.Offset < b.Offset { + return -1 + } + if a.Offset > b.Offset { + return 1 + } + return 0 + } + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Column < b.Column { + return -1 + } + if a.Column > b.Column { + return 1 + } + return 0 +} + +func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +func (s Span) IsValid() bool { return s.v.Start.isValid() } +func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +func (s Span) URI() URI { return s.v.URI } +func (s Span) Start() Point { return Point{s.v.Start} } +func (s Span) End() Point { return Point{s.v.End} } +func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } + +func (p Point) HasPosition() bool { return p.v.hasPosition() } +func (p Point) HasOffset() bool { return p.v.hasOffset() } +func (p Point) IsValid() bool { return p.v.isValid() } +func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +func (p Point) Line() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Line +} +func (p Point) Column() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Column +} +func (p Point) Offset() int { + if !p.v.hasOffset() { + panic(fmt.Errorf("offset not set in %v", p.v)) + } + return p.v.Offset +} + +func (p point) hasPosition() bool { return p.Line > 0 } +func (p point) hasOffset() bool { return p.Offset >= 0 } +func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +func (p point) isZero() bool { + return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +} + +func (s *span) clean() { + //this presumes the points are already clean + if !s.End.isValid() || (s.End == point{}) { + s.End = s.Start + } +} + +func (p *point) clean() { + if p.Line < 0 { + p.Line = 0 + } + if p.Column <= 0 { + if p.Line > 0 { + p.Column = 1 + } else { + p.Column = 0 + } + } + if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { + p.Offset = -1 + } +} + +// Format implements fmt.Formatter to print the Location in a standard form. +// The format produced is one that can be read back in using Parse. +func (s Span) Format(f fmt.State, c rune) { + fullForm := f.Flag('+') + preferOffset := f.Flag('#') + // we should always have a uri, simplify if it is file format + //TODO: make sure the end of the uri is unambiguous + uri := string(s.v.URI) + if c == 'f' { + uri = path.Base(uri) + } else if !fullForm { + uri = s.v.URI.Filename() + } + fmt.Fprint(f, uri) + if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { + return + } + // see which bits of start to write + printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) + printLine := s.HasPosition() && (fullForm || !printOffset) + printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) + fmt.Fprint(f, ":") + if printLine { + fmt.Fprintf(f, "%d", s.v.Start.Line) + } + if printColumn { + fmt.Fprintf(f, ":%d", s.v.Start.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.Start.Offset) + } + // start is written, do we need end? + if s.IsPoint() { + return + } + // we don't print the line if it did not change + printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) + fmt.Fprint(f, "-") + if printLine { + fmt.Fprintf(f, "%d", s.v.End.Line) + } + if printColumn { + if printLine { + fmt.Fprint(f, ":") + } + fmt.Fprintf(f, "%d", s.v.End.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.End.Offset) + } +} + +func (s Span) WithPosition(c Converter) (Span, error) { + if err := s.update(c, true, false); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithOffset(c Converter) (Span, error) { + if err := s.update(c, false, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithAll(c Converter) (Span, error) { + if err := s.update(c, true, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s *Span) update(c Converter, withPos, withOffset bool) error { + if !s.IsValid() { + return fmt.Errorf("cannot add information to an invalid span") + } + if withPos && !s.HasPosition() { + if err := s.v.Start.updatePosition(c); err != nil { + return err + } + if s.v.End.Offset == s.v.Start.Offset { + s.v.End = s.v.Start + } else if err := s.v.End.updatePosition(c); err != nil { + return err + } + } + if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) { + if err := s.v.Start.updateOffset(c); err != nil { + return err + } + if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column { + s.v.End.Offset = s.v.Start.Offset + } else if err := s.v.End.updateOffset(c); err != nil { + return err + } + } + return nil +} + +func (p *point) updatePosition(c Converter) error { + line, col, err := c.ToPosition(p.Offset) + if err != nil { + return err + } + p.Line = line + p.Column = col + return nil +} + +func (p *point) updateOffset(c Converter) error { + offset, err := c.ToOffset(p.Line, p.Column) + if err != nil { + return err + } + p.Offset = offset + return nil +} diff --git a/vendor/golang.org/x/tools/internal/span/token.go b/vendor/golang.org/x/tools/internal/span/token.go new file mode 100644 index 000000000..ce44541b2 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token.go @@ -0,0 +1,151 @@ +// Copyright 2019 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 span + +import ( + "fmt" + "go/token" +) + +// Range represents a source code range in token.Pos form. +// It also carries the FileSet that produced the positions, so that it is +// self contained. +type Range struct { + FileSet *token.FileSet + Start token.Pos + End token.Pos +} + +// TokenConverter is a Converter backed by a token file set and file. +// It uses the file set methods to work out the conversions, which +// makes it fast and does not require the file contents. +type TokenConverter struct { + fset *token.FileSet + file *token.File +} + +// NewRange creates a new Range from a FileSet and two positions. +// To represent a point pass a 0 as the end pos. +func NewRange(fset *token.FileSet, start, end token.Pos) Range { + return Range{ + FileSet: fset, + Start: start, + End: end, + } +} + +// NewTokenConverter returns an implementation of Converter backed by a +// token.File. +func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter { + return &TokenConverter{fset: fset, file: f} +} + +// NewContentConverter returns an implementation of Converter for the +// given file content. +func NewContentConverter(filename string, content []byte) *TokenConverter { + fset := token.NewFileSet() + f := fset.AddFile(filename, -1, len(content)) + f.SetLinesForContent(content) + return &TokenConverter{fset: fset, file: f} +} + +// IsPoint returns true if the range represents a single point. +func (r Range) IsPoint() bool { + return r.Start == r.End +} + +// Span converts a Range to a Span that represents the Range. +// It will fill in all the members of the Span, calculating the line and column +// information. +func (r Range) Span() (Span, error) { + f := r.FileSet.File(r.Start) + if f == nil { + return Span{}, fmt.Errorf("file not found in FileSet") + } + s := Span{v: span{URI: FileURI(f.Name())}} + var err error + s.v.Start.Offset, err = offset(f, r.Start) + if err != nil { + return Span{}, err + } + if r.End.IsValid() { + s.v.End.Offset, err = offset(f, r.End) + if err != nil { + return Span{}, err + } + } + s.v.Start.clean() + s.v.End.clean() + s.v.clean() + converter := NewTokenConverter(r.FileSet, f) + return s.WithPosition(converter) +} + +// offset is a copy of the Offset function in go/token, but with the adjustment +// that it does not panic on invalid positions. +func offset(f *token.File, pos token.Pos) (int, error) { + if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() { + return 0, fmt.Errorf("invalid pos") + } + return int(pos) - f.Base(), nil +} + +// Range converts a Span to a Range that represents the Span for the supplied +// File. +func (s Span) Range(converter *TokenConverter) (Range, error) { + s, err := s.WithOffset(converter) + if err != nil { + return Range{}, err + } + // go/token will panic if the offset is larger than the file's size, + // so check here to avoid panicking. + if s.Start().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size()) + } + if s.End().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size()) + } + return Range{ + FileSet: converter.fset, + Start: converter.file.Pos(s.Start().Offset()), + End: converter.file.Pos(s.End().Offset()), + }, nil +} + +func (l *TokenConverter) ToPosition(offset int) (int, int, error) { + if offset > l.file.Size() { + return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size()) + } + pos := l.file.Pos(offset) + p := l.fset.Position(pos) + if offset == l.file.Size() { + return p.Line + 1, 1, nil + } + return p.Line, p.Column, nil +} + +func (l *TokenConverter) ToOffset(line, col int) (int, error) { + if line < 0 { + return -1, fmt.Errorf("line is not valid") + } + lineMax := l.file.LineCount() + 1 + if line > lineMax { + return -1, fmt.Errorf("line is beyond end of file %v", lineMax) + } else if line == lineMax { + if col > 1 { + return -1, fmt.Errorf("column is beyond end of file") + } + // at the end of the file, allowing for a trailing eol + return l.file.Size(), nil + } + pos := lineStart(l.file, line) + if !pos.IsValid() { + return -1, fmt.Errorf("line is not in file") + } + // we assume that column is in bytes here, and that the first byte of a + // line is at column 1 + pos += token.Pos(col - 1) + return offset(l.file, pos) +} diff --git a/vendor/golang.org/x/tools/internal/span/token111.go b/vendor/golang.org/x/tools/internal/span/token111.go new file mode 100644 index 000000000..bf7a5406b --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token111.go @@ -0,0 +1,39 @@ +// Copyright 2019 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. + +// +build !go1.12 + +package span + +import ( + "go/token" +) + +// lineStart is the pre-Go 1.12 version of (*token.File).LineStart. For Go +// versions <= 1.11, we borrow logic from the analysisutil package. +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + // Use binary search to find the start offset of this line. + + min := 0 // inclusive + max := f.Size() // exclusive + for { + offset := (min + max) / 2 + pos := f.Pos(offset) + posn := f.Position(pos) + if posn.Line == line { + return pos - (token.Pos(posn.Column) - 1) + } + + if min+1 >= max { + return token.NoPos + } + + if posn.Line < line { + min = offset + } else { + max = offset + } + } +} diff --git a/vendor/golang.org/x/tools/internal/span/token112.go b/vendor/golang.org/x/tools/internal/span/token112.go new file mode 100644 index 000000000..017aec9c1 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/token112.go @@ -0,0 +1,16 @@ +// Copyright 2019 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. + +// +build go1.12 + +package span + +import ( + "go/token" +) + +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + return f.LineStart(line) +} diff --git a/vendor/golang.org/x/tools/internal/span/uri.go b/vendor/golang.org/x/tools/internal/span/uri.go new file mode 100644 index 000000000..e05a9e6ef --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/uri.go @@ -0,0 +1,152 @@ +// Copyright 2019 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 span + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +const fileScheme = "file" + +// URI represents the full URI for a file. +type URI string + +// Filename returns the file path for the given URI. +// It is an error to call this on a URI that is not a valid filename. +func (uri URI) Filename() string { + filename, err := filename(uri) + if err != nil { + panic(err) + } + return filepath.FromSlash(filename) +} + +func filename(uri URI) (string, error) { + if uri == "" { + return "", nil + } + u, err := url.ParseRequestURI(string(uri)) + if err != nil { + return "", err + } + if u.Scheme != fileScheme { + return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) + } + if isWindowsDriveURI(u.Path) { + u.Path = u.Path[1:] + } + return u.Path, nil +} + +// NewURI returns a span URI for the string. +// It will attempt to detect if the string is a file path or uri. +func NewURI(s string) URI { + if u, err := url.PathUnescape(s); err == nil { + s = u + } + if strings.HasPrefix(s, fileScheme+"://") { + return URI(s) + } + return FileURI(s) +} + +func CompareURI(a, b URI) int { + if equalURI(a, b) { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func equalURI(a, b URI) bool { + if a == b { + return true + } + // If we have the same URI basename, we may still have the same file URIs. + if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) { + return false + } + fa, err := filename(a) + if err != nil { + return false + } + fb, err := filename(b) + if err != nil { + return false + } + // Stat the files to check if they are equal. + infoa, err := os.Stat(filepath.FromSlash(fa)) + if err != nil { + return false + } + infob, err := os.Stat(filepath.FromSlash(fb)) + if err != nil { + return false + } + return os.SameFile(infoa, infob) +} + +// FileURI returns a span URI for the supplied file path. +// It will always have the file scheme. +func FileURI(path string) URI { + if path == "" { + return "" + } + // Handle standard library paths that contain the literal "$GOROOT". + // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. + const prefix = "$GOROOT" + if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { + suffix := path[len(prefix):] + path = runtime.GOROOT() + suffix + } + if !isWindowsDrivePath(path) { + if abs, err := filepath.Abs(path); err == nil { + path = abs + } + } + // Check the file path again, in case it became absolute. + if isWindowsDrivePath(path) { + path = "/" + path + } + path = filepath.ToSlash(path) + u := url.URL{ + Scheme: fileScheme, + Path: path, + } + uri := u.String() + if unescaped, err := url.PathUnescape(uri); err == nil { + uri = unescaped + } + return URI(uri) +} + +// isWindowsDrivePath returns true if the file path is of the form used by +// Windows. We check if the path begins with a drive letter, followed by a ":". +func isWindowsDrivePath(path string) bool { + if len(path) < 4 { + return false + } + return unicode.IsLetter(rune(path[0])) && path[1] == ':' +} + +// isWindowsDriveURI returns true if the file URI is of the format used by +// Windows URIs. The url.Parse package does not specially handle Windows paths +// (see https://golang.org/issue/6027). We check if the URI path has +// a drive prefix (e.g. "/C:"). If so, we trim the leading "/". +func isWindowsDriveURI(uri string) bool { + if len(uri) < 4 { + return false + } + return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +} diff --git a/vendor/golang.org/x/tools/internal/span/utf16.go b/vendor/golang.org/x/tools/internal/span/utf16.go new file mode 100644 index 000000000..561b3fa50 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/span/utf16.go @@ -0,0 +1,94 @@ +// Copyright 2019 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 span + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +// ToUTF16Column calculates the utf16 column expressed by the point given the +// supplied file contents. +// This is used to convert from the native (always in bytes) column +// representation and the utf16 counts used by some editors. +func ToUTF16Column(p Point, content []byte) (int, error) { + if content == nil { + return -1, fmt.Errorf("ToUTF16Column: missing content") + } + if !p.HasPosition() { + return -1, fmt.Errorf("ToUTF16Column: point is missing position") + } + if !p.HasOffset() { + return -1, fmt.Errorf("ToUTF16Column: point is missing offset") + } + offset := p.Offset() // 0-based + colZero := p.Column() - 1 // 0-based + if colZero == 0 { + // 0-based column 0, so it must be chr 1 + return 1, nil + } else if colZero < 0 { + return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero) + } + // work out the offset at the start of the line using the column + lineOffset := offset - colZero + if lineOffset < 0 || offset > len(content) { + return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content)) + } + // Use the offset to pick out the line start. + // This cannot panic: offset > len(content) and lineOffset < offset. + start := content[lineOffset:] + + // Now, truncate down to the supplied column. + start = start[:colZero] + + // and count the number of utf16 characters + // in theory we could do this by hand more efficiently... + return len(utf16.Encode([]rune(string(start)))) + 1, nil +} + +// FromUTF16Column advances the point by the utf16 character offset given the +// supplied line contents. +// This is used to convert from the utf16 counts used by some editors to the +// native (always in bytes) column representation. +func FromUTF16Column(p Point, chr int, content []byte) (Point, error) { + if !p.HasOffset() { + return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset") + } + // if chr is 1 then no adjustment needed + if chr <= 1 { + return p, nil + } + if p.Offset() >= len(content) { + return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content)) + } + remains := content[p.Offset():] + // scan forward the specified number of characters + for count := 1; count < chr; count++ { + if len(remains) <= 0 { + return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content") + } + r, w := utf8.DecodeRune(remains) + if r == '\n' { + // Per the LSP spec: + // + // > If the character value is greater than the line length it + // > defaults back to the line length. + break + } + remains = remains[w:] + if r >= 0x10000 { + // a two point rune + count++ + // if we finished in a two point rune, do not advance past the first + if count >= chr { + break + } + } + p.v.Column += w + p.v.Offset += w + } + return p, nil +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/Makefile b/vendor/gopkg.in/go-playground/validator.v9/Makefile index aeeee9da9..b912cae91 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/Makefile +++ b/vendor/gopkg.in/go-playground/validator.v9/Makefile @@ -1,11 +1,13 @@ GOCMD=go linters-install: - $(GOCMD) get -u github.com/alecthomas/gometalinter - gometalinter --install + @golangci-lint --version >/dev/null 2>&1 || { \ + echo "installing linting tools..."; \ + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.19.1; \ + } lint: linters-install - gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=maligned --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./... + golangci-lint run test: $(GOCMD) test -cover -race ./... diff --git a/vendor/gopkg.in/go-playground/validator.v9/README.md b/vendor/gopkg.in/go-playground/validator.v9/README.md index ba6a74e65..b55f3ecfd 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/README.md +++ b/vendor/gopkg.in/go-playground/validator.v9/README.md @@ -1,7 +1,9 @@ +**NOTICE:** v9 has entered maintenance status as of 2019-12-24. Please make all new functionality PR's against master. + Package validator ================ [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -![Project status](https://img.shields.io/badge/version-9.29.1-green.svg) +![Project status](https://img.shields.io/badge/version-9.31.0-green.svg) [![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) diff --git a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go index 338cddd75..cfc5686d4 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go +++ b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go @@ -103,6 +103,7 @@ var ( "rgba": isRGBA, "hsl": isHSL, "hsla": isHSLA, + "e164": isE164, "email": isEmail, "url": isURL, "uri": isURI, @@ -224,14 +225,28 @@ func isOneOf(fl FieldLevel) bool { func isUnique(fl FieldLevel) bool { field := fl.Field() + param := fl.Param() v := reflect.ValueOf(struct{}{}) switch field.Kind() { case reflect.Slice, reflect.Array: - m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + if param == "" { + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(field.Index(i), v) + } + return field.Len() == m.Len() + } + + sf, ok := field.Type().Elem().FieldByName(param) + if !ok { + panic(fmt.Sprintf("Bad field name %s", param)) + } + + m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type())) for i := 0; i < field.Len(); i++ { - m.SetMapIndex(field.Index(i), v) + m.SetMapIndex(field.Index(i).FieldByName(param), v) } return field.Len() == m.Len() case reflect.Map: @@ -1219,6 +1234,11 @@ func isFile(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// IsE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. +func isE164(fl FieldLevel) bool { + return e164Regex.MatchString(fl.Field().String()) +} + // IsEmail is the validation function for validating if the current field's value is a valid email address. func isEmail(fl FieldLevel) bool { return emailRegex.MatchString(fl.Field().String()) @@ -1301,19 +1321,7 @@ func isDefault(fl FieldLevel) bool { // HasValue is the validation function for validating if the current field's value is not the default static value. func hasValue(fl FieldLevel) bool { - return requireCheckFieldKind(fl, "") -} - -// requireCheckField is a func for check field kind -func requireCheckFieldKind(fl FieldLevel, param string) bool { field := fl.Field() - if len(param) > 0 { - if fl.Parent().Kind() == reflect.Ptr { - field = fl.Parent().Elem().FieldByName(param) - } else { - field = fl.Parent().FieldByName(param) - } - } switch field.Kind() { case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() @@ -1325,79 +1333,73 @@ func requireCheckFieldKind(fl FieldLevel, param string) bool { } } +// requireCheckField is a func for check field kind +func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { + field := fl.Field() + kind := field.Kind() + var nullable, found bool + if len(param) > 0 { + field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param) + if !found { + return defaultNotFoundValue + } + } + switch kind { + case reflect.Invalid: + return defaultNotFoundValue + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return field.IsNil() + default: + if nullable && field.Interface() != nil { + return false + } + return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface() + } +} + // RequiredWith is the validation function // The field under validation must be present and not empty only if any of the other specified fields are present. func requiredWith(fl FieldLevel) bool { - params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if requireCheckFieldKind(fl, param) { - return requireCheckFieldKind(fl, "") + if !requireCheckFieldKind(fl, param, true) { + return hasValue(fl) } } - return true } // RequiredWithAll is the validation function // The field under validation must be present and not empty only if all of the other specified fields are present. func requiredWithAll(fl FieldLevel) bool { - - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if !requireCheckFieldKind(fl, param) { - isValidateCurrentField = false + if requireCheckFieldKind(fl, param, true) { + return true } } - - if isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - - return true + return hasValue(fl) } // RequiredWithout is the validation function // The field under validation must be present and not empty only when any of the other specified fields are not present. func requiredWithout(fl FieldLevel) bool { - - isValidateCurrentField := false - params := parseOneOfParam2(fl.Param()) - for _, param := range params { - - if requireCheckFieldKind(fl, param) { - isValidateCurrentField = true - } + if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) { + return hasValue(fl) } - - if !isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - return true } // RequiredWithoutAll is the validation function // The field under validation must be present and not empty only when all of the other specified fields are not present. func requiredWithoutAll(fl FieldLevel) bool { - - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if requireCheckFieldKind(fl, param) { - isValidateCurrentField = false + if !requireCheckFieldKind(fl, param, true) { + return true } } - - if isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - - return true + return hasValue(fl) } // IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. diff --git a/vendor/gopkg.in/go-playground/validator.v9/cache.go b/vendor/gopkg.in/go-playground/validator.v9/cache.go index a7a4202f6..0d18d6ec4 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/cache.go +++ b/vendor/gopkg.in/go-playground/validator.v9/cache.go @@ -39,9 +39,7 @@ func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) { } func (sc *structCache) Set(key reflect.Type, value *cStruct) { - m := sc.m.Load().(map[reflect.Type]*cStruct) - nm := make(map[reflect.Type]*cStruct, len(m)+1) for k, v := range m { nm[k] = v @@ -61,9 +59,7 @@ func (tc *tagCache) Get(key string) (c *cTag, found bool) { } func (tc *tagCache) Set(key string, value *cTag) { - m := tc.m.Load().(map[string]*cTag) - nm := make(map[string]*cTag, len(m)+1) for k, v := range m { nm[k] = v @@ -87,22 +83,22 @@ type cField struct { } type cTag struct { - tag string - aliasTag string - actualAliasTag string - param string - keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation - next *cTag - fn FuncCtx - typeof tagType - hasTag bool - hasAlias bool - hasParam bool // true if parameter used eg. eq= where the equal sign has been set - isBlockEnd bool // indicates the current tag represents the last validation in the block + tag string + aliasTag string + actualAliasTag string + param string + keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation + next *cTag + fn FuncCtx + typeof tagType + hasTag bool + hasAlias bool + hasParam bool // true if parameter used eg. eq= where the equal sign has been set + isBlockEnd bool // indicates the current tag represents the last validation in the block + runValidationWhenNil bool } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { - v.structCache.lock.Lock() defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! @@ -141,9 +137,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr customName = fld.Name if v.hasTagNameFunc { - name := v.tagNameFunc(fld) - if len(name) > 0 { customName = name } @@ -168,23 +162,17 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr namesEqual: fld.Name == customName, }) } - v.structCache.Set(typ, cs) - return cs } func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { - var t string - var ok bool noAlias := len(alias) == 0 tags := strings.Split(tag, tagSeparator) for i := 0; i < len(tags); i++ { - t = tags[i] - if noAlias { alias = t } @@ -198,14 +186,13 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s current.next, current = next, curr } - continue } var prevTag tagType if i == 0 { - current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault} firstCtag = current } else { prevTag = current.typeof @@ -214,7 +201,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s } switch t { - case diveTag: current.typeof = typeDive continue @@ -270,18 +256,14 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s continue default: - if t == isdefault { current.typeof = typeIsDefault } - // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" orVals := strings.Split(t, orSeparator) for j := 0; j < len(orVals); j++ { - vals := strings.SplitN(orVals[j], tagKeySeparator, 2) - if noAlias { alias = vals[0] current.aliasTag = alias @@ -300,7 +282,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) } - if current.fn, ok = v.validations[current.tag]; !ok { + if wrapper, ok := v.validations[current.tag]; ok { + current.fn = wrapper.fn + current.runValidationWhenNil = wrapper.runValidatinOnNil + } else { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName))) } diff --git a/vendor/gopkg.in/go-playground/validator.v9/doc.go b/vendor/gopkg.in/go-playground/validator.v9/doc.go index e0396cb44..7ad9dea7f 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/doc.go +++ b/vendor/gopkg.in/go-playground/validator.v9/doc.go @@ -585,9 +585,15 @@ Unique For arrays & slices, unique will ensure that there are no duplicates. For maps, unique will ensure that there are no duplicate values. +For slices of struct, unique will ensure that there are no duplicate values +in a field of the struct specified via a parameter. + // For arrays, slices, and maps: Usage: unique + // For slices of struct: + Usage: unique=field + Alpha Only This validates that a string value contains ASCII alpha characters only @@ -1058,6 +1064,35 @@ Validator notes: And the best reason, you can submit a pull request and we can keep on adding to the validation library of this package! +Non standard validators + +A collection of validation rules that are frequently needed but are more +complex than the ones found in the baked in validators. +A non standard validator must be registered manually like you would +with your own custom validation functions. + +Example of registration and use: + + type Test struct { + TestField string `validate:"yourtag"` + } + + t := &Test{ + TestField: "Test" + } + + validate := validator.New() + validate.RegisterValidation("yourtag", validators.NotBlank) + +Here is a list of the current non standard validators: + + NotBlank + This validates that the value is not blank or with length zero. + For strings ensures they do not contain only spaces. For channels, maps, slices and arrays + ensures they don't have zero length. For others, a non empty value is required. + + Usage: notblank + Panics This package panics when bad input is provided, this is by design, bad code like @@ -1072,30 +1107,5 @@ that should not make it to production. } validate.Struct(t) // this will panic - -Non standard validators - -A collection of validation rules that are frequently needed but are more -complex than the ones found in the baked in validators. -A non standard validator must be registered manually using any tag you like. -See below examples of registration and use. - - type Test struct { - TestField string `validate:"yourtag"` - } - - t := &Test{ - TestField: "Test" - } - - validate := validator.New() - validate.RegisterValidation("yourtag", validations.ValidatorName) - - NotBlank - This validates that the value is not blank or with length zero. - For strings ensures they do not contain only spaces. For channels, maps, slices and arrays - ensures they don't have zero length. For others, a non empty value is required. - - Usage: notblank */ package validator diff --git a/vendor/gopkg.in/go-playground/validator.v9/field_level.go b/vendor/gopkg.in/go-playground/validator.v9/field_level.go index cbfbc1586..f0e2a9a85 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/field_level.go +++ b/vendor/gopkg.in/go-playground/validator.v9/field_level.go @@ -5,7 +5,6 @@ import "reflect" // FieldLevel contains all the information and helper functions // to validate a field type FieldLevel interface { - // returns the top level struct, if any Top() reflect.Value @@ -26,6 +25,9 @@ type FieldLevel interface { // returns param for validation against current field Param() string + // GetTag returns the current validations tag name + GetTag() string + // ExtractType gets the actual underlying type of field value. // It will dive into pointers, customTypes and return you the // underlying value and it's kind. @@ -37,7 +39,27 @@ type FieldLevel interface { // // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // could not be retrieved because it didn't exist. + // + // Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable. GetStructFieldOK() (reflect.Value, reflect.Kind, bool) + + // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for + // the field and namespace allowing more extensibility for validators. + // + // Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable. + GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) + + // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving + // the field at all. + // + // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field + // could not be retrieved because it didn't exist. + GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) + + // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for + // the field and namespace allowing more extensibility for validators. + GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) } var _ FieldLevel = new(validate) @@ -48,11 +70,16 @@ func (v *validate) Field() reflect.Value { } // FieldName returns the field's name with the tag -// name takeing precedence over the fields actual name. +// name taking precedence over the fields actual name. func (v *validate) FieldName() string { return v.cf.altName } +// GetTag returns the current validations tag name +func (v *validate) GetTag() string { + return v.ct.tag +} + // StructFieldName returns the struct field's name func (v *validate) StructFieldName() string { return v.cf.name @@ -64,6 +91,29 @@ func (v *validate) Param() string { } // GetStructFieldOK returns Param returns param for validation against current field +// +// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable. func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { + current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param) + return current, kind, found +} + +// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for +// the field and namespace allowing more extensibility for validators. +// +// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable. +func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { + current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace) + return current, kind, found +} + +// GetStructFieldOK returns Param returns param for validation against current field +func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) { return v.getStructFieldOKInternal(v.slflParent, v.ct.param) } + +// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for +// the field and namespace allowing more extensibility for validators. +func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) { + return v.getStructFieldOKInternal(val, namespace) +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/regexes.go b/vendor/gopkg.in/go-playground/validator.v9/regexes.go index 0253d7091..7ba7c7393 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/regexes.go +++ b/vendor/gopkg.in/go-playground/validator.v9/regexes.go @@ -16,6 +16,7 @@ const ( hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" @@ -61,6 +62,7 @@ var ( rgbaRegex = regexp.MustCompile(rgbaRegexString) hslRegex = regexp.MustCompile(hslRegexString) hslaRegex = regexp.MustCompile(hslaRegexString) + e164Regex = regexp.MustCompile(e164RegexString) emailRegex = regexp.MustCompile(emailRegexString) base64Regex = regexp.MustCompile(base64RegexString) base64URLRegex = regexp.MustCompile(base64URLRegexString) diff --git a/vendor/gopkg.in/go-playground/validator.v9/util.go b/vendor/gopkg.in/go-playground/validator.v9/util.go index 16a5517c9..71acbdc44 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/util.go +++ b/vendor/gopkg.in/go-playground/validator.v9/util.go @@ -57,11 +57,10 @@ BEGIN: // // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // could not be retrieved because it didn't exist. -func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) { +func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) { BEGIN: - current, kind, _ = v.ExtractType(val) - + current, kind, nullable = v.ExtractType(val) if kind == reflect.Invalid { return } @@ -112,7 +111,7 @@ BEGIN: arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) if arrIdx >= current.Len() { - return current, kind, false + return } startIdx := idx2 + 1 diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator.go b/vendor/gopkg.in/go-playground/validator.v9/validator.go index 67473f1e5..342e72e31 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator.go @@ -7,7 +7,7 @@ import ( "strconv" ) -// per validate contruct +// per validate construct type validate struct { v *Validate top reflect.Value @@ -94,7 +94,6 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { - var typ reflect.Type var kind reflect.Kind @@ -112,16 +111,13 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr } if ct.hasTag { - - v.str1 = string(append(ns, cf.altName...)) - - if v.v.hasTagNameFunc { - v.str2 = string(append(structNs, cf.name...)) - } else { - v.str2 = v.str1 - } - if kind == reflect.Invalid { + v.str1 = string(append(ns, cf.altName...)) + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } v.errs = append(v.errs, &fieldError{ v: v.v, @@ -135,27 +131,33 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr kind: kind, }, ) - return } - v.errs = append(v.errs, - &fieldError{ - v: v.v, - tag: ct.aliasTag, - actualTag: ct.tag, - ns: v.str1, - structNs: v.str2, - fieldLen: uint8(len(cf.altName)), - structfieldLen: uint8(len(cf.name)), - value: current.Interface(), - param: ct.param, - kind: kind, - typ: current.Type(), - }, - ) - - return + v.str1 = string(append(ns, cf.altName...)) + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + if !ct.runValidationWhenNil { + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: current.Type(), + }, + ) + return + } } case reflect.Struct: diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go index fc9db5a30..4a89d4061 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go @@ -13,27 +13,31 @@ import ( ) const ( - defaultTagName = "validate" - utf8HexComma = "0x2C" - utf8Pipe = "0x7C" - tagSeparator = "," - orSeparator = "|" - tagKeySeparator = "=" - structOnlyTag = "structonly" - noStructLevelTag = "nostructlevel" - omitempty = "omitempty" - isdefault = "isdefault" - skipValidationTag = "-" - diveTag = "dive" - keysTag = "keys" - endKeysTag = "endkeys" - requiredTag = "required" - namespaceSeparator = "." - leftBracket = "[" - rightBracket = "]" - restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" - restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" - restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + defaultTagName = "validate" + utf8HexComma = "0x2C" + utf8Pipe = "0x7C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + noStructLevelTag = "nostructlevel" + omitempty = "omitempty" + isdefault = "isdefault" + requiredWithoutAllTag = "required_without_all" + requiredWithoutTag = "required_without" + requiredWithTag = "required_with" + requiredWithAllTag = "required_with_all" + skipValidationTag = "-" + diveTag = "dive" + keysTag = "keys" + endKeysTag = "endkeys" + requiredTag = "required" + namespaceSeparator = "." + leftBracket = "[" + rightBracket = "]" + restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" + restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" ) var ( @@ -55,6 +59,11 @@ type CustomTypeFunc func(field reflect.Value) interface{} // TagNameFunc allows for adding of a custom tag name parser type TagNameFunc func(field reflect.StructField) string +type internalValidationFuncWrapper struct { + fn FuncCtx + runValidatinOnNil bool +} + // Validate contains the validator settings and cache type Validate struct { tagName string @@ -65,7 +74,7 @@ type Validate struct { structLevelFuncs map[reflect.Type]StructLevelFuncCtx customFuncs map[reflect.Type]CustomTypeFunc aliases map[string]string - validations map[string]FuncCtx + validations map[string]internalValidationFuncWrapper transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc tagCache *tagCache structCache *structCache @@ -83,7 +92,7 @@ func New() *Validate { v := &Validate{ tagName: defaultTagName, aliases: make(map[string]string, len(bakedInAliases)), - validations: make(map[string]FuncCtx, len(bakedInValidators)), + validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), tagCache: tc, structCache: sc, } @@ -96,8 +105,14 @@ func New() *Validate { // must copy validators for separate validations to be used in each instance for k, val := range bakedInValidators { - // no need to error check here, baked in will always be valid - _ = v.registerValidation(k, wrapFunc(val), true) + switch k { + // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour + case requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag: + _ = v.registerValidation(k, wrapFunc(val), true, true) + default: + // no need to error check here, baked in will always be valid + _ = v.registerValidation(k, wrapFunc(val), true, false) + } } v.pool = &sync.Pool{ @@ -140,18 +155,21 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { // NOTES: // - if the key already exists, the previous validation function will be replaced. // - this method is not thread-safe it is intended that these all be registered prior to any validation -func (v *Validate) RegisterValidation(tag string, fn Func) error { - return v.RegisterValidationCtx(tag, wrapFunc(fn)) +func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error { + return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...) } // RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation // allowing context.Context validation support. -func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx) error { - return v.registerValidation(tag, fn, false) +func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error { + var nilCheckable bool + if len(callValidationEvenIfNull) > 0 { + nilCheckable = callValidationEvenIfNull[0] + } + return v.registerValidation(tag, fn, false, nilCheckable) } -func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) error { - +func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error { if len(tag) == 0 { return errors.New("Function Key cannot be empty") } @@ -161,13 +179,10 @@ func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) erro } _, ok := restrictedTags[tag] - if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { panic(fmt.Sprintf(restrictedTagErr, tag)) } - - v.validations[tag] = fn - + v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable} return nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 18421c19b..d4c6a3ca6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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-20191104174740-4d42851d4d5a => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba +# 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 @@ -504,7 +504,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-20190912160710-24e19bdeb0f2 golang.org/x/net/bpf golang.org/x/net/context golang.org/x/net/html @@ -541,7 +541,7 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# golang.org/x/tools v0.0.0-20191213032237-7093a17b0467 +# golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/passes/inspect golang.org/x/tools/go/ast/astutil @@ -556,7 +556,8 @@ golang.org/x/tools/go/types/typeutil golang.org/x/tools/internal/fastwalk golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/semver -# gopkg.in/go-playground/validator.v9 v9.29.1 +golang.org/x/tools/internal/span +# gopkg.in/go-playground/validator.v9 v9.31.0 gopkg.in/go-playground/validator.v9 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2