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