From d50fee6bb2e392351a5bce6187725ab80c7420eb Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 14 May 2021 12:55:42 +0200 Subject: [PATCH] Handle connection state --- VERSION | 2 +- api/backend_subs_test.go | 90 - api/backend_test.go | 7 +- api/geth_backend.go | 16 +- cmd/populate-db/.gitignore | 2 - cmd/populate-db/flags.go | 37 - cmd/populate-db/flags_test.go | 96 - cmd/populate-db/main.go | 460 --- cmd/populate-db/signing_phrase.go | 650 ----- cmd/populate-db/sync.go | 54 - api/connection.go => connection/state.go | 38 +- .../state_test.go | 24 +- go.mod | 4 - go.sum | 9 - mailserver/cleaner_test.go | 14 +- mailserver/mailserver.go | 67 + mailserver/mailserver_db_postgres_test.go | 16 +- mailserver/mailserver_test.go | 219 +- multiaccounts/accounts/database.go | 11 +- node/get_status_node.go | 11 + node/geth_node_api_test.go | 93 +- node/geth_status_node_test.go | 8 +- params/config_test.go | 112 +- peers/peerpool_test.go | 3 - peers/topicpool_test.go | 8 +- protocol/chat.go | 4 + protocol/communities_messenger_test.go | 15 +- protocol/message_handler.go | 14 +- protocol/message_persistence.go | 10 +- protocol/messenger.go | 30 +- protocol/messenger_chats.go | 79 +- protocol/messenger_communities.go | 18 +- protocol/messenger_mailserver.go | 106 +- protocol/messenger_mute_test.go | 26 +- protocol/messenger_test.go | 132 +- protocol/persistence.go | 6 +- .../accept_request_to_join_community.go | 2 +- protocol/requests/clear_history.go | 6 +- protocol/requests/create_one_to_one_chat.go | 6 +- protocol/requests/create_profile_chat.go | 6 +- protocol/requests/create_public_chat.go | 6 +- protocol/requests/deactivate_chat.go | 2 +- .../decline_request_to_join_community.go | 2 +- .../requests/invite_users_to_community.go | 4 +- protocol/requests/share_community.go | 4 +- protocol/transport/filters_manager_test.go | 6 +- protocol/transport/transport.go | 19 +- protocol/transport/waku/waku_service.go | 520 ---- .../whisper/migrations/migrations.go | 331 --- protocol/transport/whisper/whisper_service.go | 525 ---- services/ext/service.go | 16 +- services/shhext_wakuext_test.go | 69 - services/wakuext/api.go | 57 + services/wakuext/api_test.go | 143 - t/benchmarks/doc_test.go | 52 - t/benchmarks/flags_test.go | 27 - t/benchmarks/mailserver_test.go | 142 - t/benchmarks/messages_test.go | 97 - t/benchmarks/utils_test.go | 59 - t/devtests/devnode.go | 2 +- t/e2e/node/manager_test.go | 36 +- t/e2e/rpc/rpc_test.go | 23 - t/e2e/services/status_api_test.go | 186 -- t/e2e/suites.go | 12 +- t/e2e/testing.go | 2 +- t/utils/utils.go | 7 - vendor/github.com/mattn/go-pointer/LICENSE | 21 - vendor/github.com/mattn/go-pointer/README.md | 29 - .../mattn/go-pointer/_example/callback.h | 9 - vendor/github.com/mattn/go-pointer/doc.go | 1 - vendor/github.com/mattn/go-pointer/pointer.go | 57 - .../github.com/stretchr/objx/.codeclimate.yml | 13 - vendor/github.com/stretchr/objx/.gitignore | 11 - vendor/github.com/stretchr/objx/.travis.yml | 25 - vendor/github.com/stretchr/objx/Gopkg.lock | 30 - vendor/github.com/stretchr/objx/Gopkg.toml | 8 - vendor/github.com/stretchr/objx/LICENSE | 22 - vendor/github.com/stretchr/objx/README.md | 80 - vendor/github.com/stretchr/objx/Taskfile.yml | 32 - vendor/github.com/stretchr/objx/accessors.go | 148 - vendor/github.com/stretchr/objx/constants.go | 13 - .../github.com/stretchr/objx/conversions.go | 108 - vendor/github.com/stretchr/objx/doc.go | 66 - vendor/github.com/stretchr/objx/map.go | 190 -- vendor/github.com/stretchr/objx/mutations.go | 77 - vendor/github.com/stretchr/objx/security.go | 12 - vendor/github.com/stretchr/objx/tests.go | 17 - .../stretchr/objx/type_specific_codegen.go | 2501 ----------------- vendor/github.com/stretchr/objx/value.go | 53 - .../github.com/stretchr/testify/mock/doc.go | 44 - .../github.com/stretchr/testify/mock/mock.go | 917 ------ vendor/golang.org/x/sync/AUTHORS | 3 - vendor/golang.org/x/sync/CONTRIBUTORS | 3 - vendor/golang.org/x/sync/LICENSE | 27 - vendor/golang.org/x/sync/PATENTS | 22 - vendor/golang.org/x/sync/syncmap/go19.go | 17 - vendor/golang.org/x/sync/syncmap/map.go | 8 - vendor/golang.org/x/sync/syncmap/pre_go19.go | 370 --- vendor/modules.txt | 7 - 99 files changed, 585 insertions(+), 9216 deletions(-) delete mode 100644 cmd/populate-db/.gitignore delete mode 100644 cmd/populate-db/flags.go delete mode 100644 cmd/populate-db/flags_test.go delete mode 100644 cmd/populate-db/main.go delete mode 100644 cmd/populate-db/signing_phrase.go delete mode 100644 cmd/populate-db/sync.go rename api/connection.go => connection/state.go (68%) rename api/connection_test.go => connection/state_test.go (63%) delete mode 100644 protocol/transport/waku/waku_service.go delete mode 100644 protocol/transport/whisper/migrations/migrations.go delete mode 100644 protocol/transport/whisper/whisper_service.go delete mode 100644 services/shhext_wakuext_test.go delete mode 100644 t/benchmarks/doc_test.go delete mode 100644 t/benchmarks/flags_test.go delete mode 100644 t/benchmarks/mailserver_test.go delete mode 100644 t/benchmarks/messages_test.go delete mode 100644 t/benchmarks/utils_test.go delete mode 100644 t/e2e/services/status_api_test.go delete mode 100644 vendor/github.com/mattn/go-pointer/LICENSE delete mode 100644 vendor/github.com/mattn/go-pointer/README.md delete mode 100644 vendor/github.com/mattn/go-pointer/_example/callback.h delete mode 100644 vendor/github.com/mattn/go-pointer/doc.go delete mode 100644 vendor/github.com/mattn/go-pointer/pointer.go delete mode 100644 vendor/github.com/stretchr/objx/.codeclimate.yml delete mode 100644 vendor/github.com/stretchr/objx/.gitignore delete mode 100644 vendor/github.com/stretchr/objx/.travis.yml delete mode 100644 vendor/github.com/stretchr/objx/Gopkg.lock delete mode 100644 vendor/github.com/stretchr/objx/Gopkg.toml delete mode 100644 vendor/github.com/stretchr/objx/LICENSE delete mode 100644 vendor/github.com/stretchr/objx/README.md delete mode 100644 vendor/github.com/stretchr/objx/Taskfile.yml delete mode 100644 vendor/github.com/stretchr/objx/accessors.go delete mode 100644 vendor/github.com/stretchr/objx/constants.go delete mode 100644 vendor/github.com/stretchr/objx/conversions.go delete mode 100644 vendor/github.com/stretchr/objx/doc.go delete mode 100644 vendor/github.com/stretchr/objx/map.go delete mode 100644 vendor/github.com/stretchr/objx/mutations.go delete mode 100644 vendor/github.com/stretchr/objx/security.go delete mode 100644 vendor/github.com/stretchr/objx/tests.go delete mode 100644 vendor/github.com/stretchr/objx/type_specific_codegen.go delete mode 100644 vendor/github.com/stretchr/objx/value.go delete mode 100644 vendor/github.com/stretchr/testify/mock/doc.go delete mode 100644 vendor/github.com/stretchr/testify/mock/mock.go delete mode 100644 vendor/golang.org/x/sync/AUTHORS delete mode 100644 vendor/golang.org/x/sync/CONTRIBUTORS delete mode 100644 vendor/golang.org/x/sync/LICENSE delete mode 100644 vendor/golang.org/x/sync/PATENTS delete mode 100644 vendor/golang.org/x/sync/syncmap/go19.go delete mode 100644 vendor/golang.org/x/sync/syncmap/map.go delete mode 100644 vendor/golang.org/x/sync/syncmap/pre_go19.go diff --git a/VERSION b/VERSION index b88fccfdc..870aec4a7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.78.1 +0.79.0 diff --git a/api/backend_subs_test.go b/api/backend_subs_test.go index 14c061695..833bff50e 100644 --- a/api/backend_subs_test.go +++ b/api/backend_subs_test.go @@ -97,98 +97,8 @@ func TestSubscriptionPendingTransaction(t *testing.T) { } } -func TestSubscriptionWhisperEnvelopes(t *testing.T) { - backend := NewGethStatusBackend() - defer func() { - err := backend.StopNode() - if err != node.ErrNoRunningNode { - require.NoError(t, err) - } - }() - - initNodeAndLogin(t, backend) - - signals := make(chan string) - defer func() { - signal.ResetDefaultNodeNotificationHandler() - close(signals) - }() - - signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) { - signals <- jsonEvent - }) - - topic := "0x12341234" - payload := "0x12312312" - - shhGenSymKeyJSONResponse, err := backend.CallPrivateRPC(`{"jsonrpc":"2.0","method":"shh_generateSymKeyFromPassword","params":["test"],"id":119}`) - require.NoError(t, err) - symKeyID := extractResult(t, shhGenSymKeyJSONResponse) - - subID := createSubscription(t, backend, fmt.Sprintf(`"shh_newMessageFilter", [{ "symKeyID": "%s", "topics": ["%s"] }]`, symKeyID, topic)) - - sendMessageFmt := ` - { - "jsonrpc": "2.0", - "method": "shh_post", - "params": [{ - "ttl": 7, - "symKeyID": "%s", - "topic": "%s", - "powTarget": 2.01, - "powTime": 2, - "payload": "%s" - }], - "id":11 - }` - - numberOfEnvelopes := 5 - - for i := 0; i < numberOfEnvelopes; i++ { - _, err = backend.CallPrivateRPC(fmt.Sprintf(sendMessageFmt, symKeyID, topic, payload)) - require.NoError(t, err) - } - - var ( - total int - after = time.After(2 * time.Second) - exit bool - ) - for !exit { - select { - case event := <-signals: - total += validateShhEvent(t, event, subID, topic, payload) - if total == numberOfEnvelopes { - exit = true - } - case <-after: - exit = true - } - } - require.Equal(t, numberOfEnvelopes, total, "total number of envelopes must be equal to sent number of envelopes") -} - // * * * * * * * * * * utility methods below * * * * * * * * * * * -func validateShhEvent(t *testing.T, jsonEvent string, expectedSubID string, topic string, payload string) int { - result := struct { - Event signal.SubscriptionDataEvent `json:"event"` - Type string `json:"type"` - }{} - - require.NoError(t, json.Unmarshal([]byte(jsonEvent), &result)) - - require.Equal(t, signal.EventSubscriptionsData, result.Type) - require.Equal(t, expectedSubID, result.Event.FilterID) - - for _, item := range result.Event.Data { - dict := item.(map[string]interface{}) - require.Equal(t, dict["topic"], topic) - require.Equal(t, dict["payload"], payload) - } - return len(result.Event.Data) -} - func validateTxEvent(t *testing.T, expectedSubID string, jsonEvent string, txID string) { result := struct { Event signal.SubscriptionDataEvent `json:"event"` diff --git a/api/backend_test.go b/api/backend_test.go index 2ac499b34..22b0c4dca 100644 --- a/api/backend_test.go +++ b/api/backend_test.go @@ -20,6 +20,7 @@ import ( gethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/status-im/status-go/account" + "github.com/status-im/status-go/connection" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/multiaccounts" @@ -231,7 +232,7 @@ func TestBackendAccountsConcurrently(t *testing.T) { } func TestBackendConnectionChangesConcurrently(t *testing.T) { - connections := [...]string{wifi, cellular, unknown} + connections := [...]string{connection.Wifi, connection.Cellular, connection.Unknown} backend := NewGethStatusBackend() count := 3 @@ -251,10 +252,10 @@ func TestBackendConnectionChangesConcurrently(t *testing.T) { func TestBackendConnectionChangesToOffline(t *testing.T) { b := NewGethStatusBackend() - b.ConnectionChange(none, false) + b.ConnectionChange(connection.None, false) assert.True(t, b.connectionState.Offline) - b.ConnectionChange(wifi, false) + b.ConnectionChange(connection.Wifi, false) assert.False(t, b.connectionState.Offline) b.ConnectionChange("unknown-state", false) diff --git a/api/geth_backend.go b/api/geth_backend.go index 64c842e14..093331476 100644 --- a/api/geth_backend.go +++ b/api/geth_backend.go @@ -23,6 +23,7 @@ import ( "github.com/status-im/status-go/account" "github.com/status-im/status-go/appdatabase" "github.com/status-im/status-go/appmetrics" + "github.com/status-im/status-go/connection" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/logutils" @@ -86,7 +87,7 @@ type GethStatusBackend struct { account *multiaccounts.Account accountManager *account.GethManager transactor *transactions.Transactor - connectionState connectionState + connectionState connection.State appState appState selectedAccountKeyID string log log.Logger @@ -967,17 +968,21 @@ func (b *GethStatusBackend) ConnectionChange(typ string, expensive bool) { b.mu.Lock() defer b.mu.Unlock() - state := connectionState{ - Type: newConnectionType(typ), + state := connection.State{ + Type: connection.NewConnectionType(typ), Expensive: expensive, } - if typ == none { + if typ == connection.None { state.Offline = true } b.log.Info("Network state change", "old", b.connectionState, "new", state) b.connectionState = state + err := b.statusNode.ConnectionChanged(state) + if err != nil { + b.log.Error("failed to notify of connection changed", "err", err) + } // logic of handling state changes here // restart node? force peers reconnect? etc @@ -1187,6 +1192,9 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error { if err := st.InitProtocol(identity, b.appDB, b.multiaccountsDB, acc, logutils.ZapLogger()); err != nil { return err } + + // Set initial connection state + st.ConnectionChanged(b.connectionState) } return nil } diff --git a/cmd/populate-db/.gitignore b/cmd/populate-db/.gitignore deleted file mode 100644 index 647a4d420..000000000 --- a/cmd/populate-db/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -populate-db -tmp/ diff --git a/cmd/populate-db/flags.go b/cmd/populate-db/flags.go deleted file mode 100644 index be519d51c..000000000 --- a/cmd/populate-db/flags.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path" - "strings" -) - -// configFlags represents an array of JSON configuration files passed to a command line utility -type configFlags []string - -func (f *configFlags) String() string { - return strings.Join(*f, ", ") -} - -func (f *configFlags) Set(value string) error { - if !path.IsAbs(value) { - // Convert to absolute path - cwd, err := os.Getwd() - if err != nil { - return err - } - value = path.Join(cwd, value) - } - - // Check that the file exists - stat, err := os.Stat(value) - if err != nil { - return err - } - if stat.IsDir() { - return fmt.Errorf("path does not represent a file: %s", value) - } - *f = append(*f, value) - return nil -} diff --git a/cmd/populate-db/flags_test.go b/cmd/populate-db/flags_test.go deleted file mode 100644 index 4331e1c73..000000000 --- a/cmd/populate-db/flags_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/status-im/status-go/params" -) - -// nolint: deadcode -func TestStatusFlag(t *testing.T) { - service := "status" - - scenarios := []struct { - ipcEnabled bool - httpEnabled bool - flag string - err error - enabled bool - public bool - }{ - // no flags - {}, - // -status=ipc -ipc - { - ipcEnabled: true, - flag: "ipc", - enabled: true, - }, - // -status=http -http - { - httpEnabled: true, - flag: "http", - enabled: true, - public: true, - }, - // -status=ipc -http -ipc - { - httpEnabled: true, - ipcEnabled: true, - flag: "ipc", - enabled: true, - }, - // -http -ipc - { - httpEnabled: true, - ipcEnabled: true, - flag: "", - }, - // -status=ipc - { - err: errStatusServiceRequiresIPC, - flag: "ipc", - }, - // -status=http - { - err: errStatusServiceRequiresHTTP, - flag: "http", - }, - // -status=bad-value - { - err: errStatusServiceInvalidFlag, - flag: "bad-value", - }, - } - - for i, s := range scenarios { - msg := fmt.Sprintf("scenario %d", i) - - c, err := params.NewNodeConfig("", 0) - require.Nil(t, err, msg) - - c.IPCEnabled = s.ipcEnabled - c.HTTPEnabled = s.httpEnabled - - c, err = configureStatusService(s.flag, c) - - if s.err != nil { - require.Equal(t, s.err, err, msg) - require.Nil(t, c, msg) - continue - } - - require.Nil(t, err, msg) - require.Equal(t, s.enabled, c.EnableStatusService, msg) - - modules := c.FormatAPIModules() - if s.public { - require.Contains(t, modules, service, msg) - } else { - require.NotContains(t, modules, service, msg) - } - } -} diff --git a/cmd/populate-db/main.go b/cmd/populate-db/main.go deleted file mode 100644 index 661564903..000000000 --- a/cmd/populate-db/main.go +++ /dev/null @@ -1,460 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "flag" - "fmt" - stdlog "log" - "math/rand" - "os" - "path/filepath" - "runtime" - - "strings" - "time" - - "github.com/google/uuid" - "golang.org/x/crypto/ssh/terminal" - - "github.com/ethereum/go-ethereum/log" - - "github.com/status-im/status-go/account/generator" - "github.com/status-im/status-go/api" - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/multiaccounts" - "github.com/status-im/status-go/multiaccounts/accounts" - //"github.com/status-im/status-go/appdatabase" - //gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/eth-node/crypto" - "github.com/status-im/status-go/logutils" - "github.com/status-im/status-go/params" - "github.com/status-im/status-go/protocol" - "github.com/status-im/status-go/protocol/common" - "github.com/status-im/status-go/protocol/identity/alias" - wakuextn "github.com/status-im/status-go/services/wakuext" -) - -type testTimeSource struct{} - -func (t *testTimeSource) GetCurrentTime() uint64 { - return uint64(time.Now().Unix()) -} - -const ( - serverClientName = "Statusd" -) - -var ( - configFiles configFlags - logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`) - logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors") - ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint") - ipcFile = flag.String("ipcfile", "", "Set IPC file path") - seedPhrase = flag.String("seed-phrase", "", "Seed phrase") - pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof") - pprofPort = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof") - version = flag.Bool("version", false, "Print version and dump configuration") - nAddedContacts = flag.Int("added-contacts", 100, "Number of added contacts to create") - nContacts = flag.Int("contacts", 100, "Number of contacts to create") - nPublicChats = flag.Int("public-chats", 5, "Number of public chats") - nOneToOneChats = flag.Int("one-to-one-chats", 5, "Number of one to one chats") - - dataDir = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data") - register = flag.Bool("register", false, "Register and make the node discoverable by other nodes") - networkID = flag.Int( - "network-id", - params.RopstenNetworkID, - fmt.Sprintf( - "A network ID: %d (Mainnet), %d (Ropsten), %d (Rinkeby), %d (Goerli)", - params.MainNetworkID, params.RopstenNetworkID, params.RinkebyNetworkID, params.GoerliNetworkID, - ), - ) - listenAddr = flag.String("addr", "", "address to bind listener to") - - syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished") -) - -// All general log messages in this package should be routed through this logger. -var logger = log.New("package", "status-go/cmd/statusd") - -func init() { - flag.Var(&configFiles, "c", "JSON configuration file(s). Multiple configuration files can be specified, and will be merged in occurrence order") -} - -// nolint:gocyclo -func main() { - colors := terminal.IsTerminal(int(os.Stdin.Fd())) - if err := logutils.OverrideRootLog(true, "ERROR", logutils.FileOptions{}, colors); err != nil { - stdlog.Fatalf("Error initializing logger: %v", err) - } - - flag.Usage = printUsage - flag.Parse() - if flag.NArg() > 0 { - printUsage() - logger.Error("Extra args in command line: %v", flag.Args()) - os.Exit(1) - } - - opts := []params.Option{} - - config, err := params.NewNodeConfigWithDefaultsAndFiles( - *dataDir, - uint64(*networkID), - opts, - configFiles, - ) - if err != nil { - printUsage() - logger.Error(err.Error()) - os.Exit(1) - } - - // Use listenAddr if and only if explicitly provided in the arguments. - // The default value is set in params.NewNodeConfigWithDefaultsAndFiles(). - if *listenAddr != "" { - config.ListenAddr = *listenAddr - } - - // enable IPC RPC - if *ipcEnabled { - config.IPCEnabled = true - config.IPCFile = *ipcFile - } - - // set up logging options - setupLogging(config) - - // We want statusd to be distinct from StatusIM client. - config.Name = serverClientName - - if *version { - printVersion(config) - return - } - - backend := api.NewGethStatusBackend() - err = ImportAccount(*seedPhrase, backend) - if err != nil { - logger.Error("failed", "err", err) - return - } - - wakuextservice, err := backend.WakuExtService() - if err != nil { - logger.Error("failed", "err", err) - return - } - - wakuext := wakuextn.NewPublicAPI(wakuextservice) - - // This will start the push notification server as well as - // the config is set to Enabled - _, err = wakuext.StartMessenger() - if err != nil { - logger.Error("failed to start messenger", "error", err) - return - } - - logger.Info("Creating added contacts") - - for i := 0; i < *nAddedContacts; i++ { - key, err := crypto.GenerateKey() - if err != nil { - logger.Error("failed", err) - return - } - - keyString := common.PubkeyToHex(&key.PublicKey) - _, err = wakuext.AddContact(context.Background(), keyString) - if err != nil { - logger.Error("failed", "err", err) - return - } - } - - logger.Info("Creating contacts") - - for i := 0; i < *nContacts; i++ { - key, err := crypto.GenerateKey() - if err != nil { - return - } - - contact, err := protocol.BuildContactFromPublicKey(&key.PublicKey) - if err != nil { - return - } - - err = wakuext.SaveContact(context.Background(), contact) - if err != nil { - return - } - } - - logger.Info("Creating public chats") - - for i := 0; i < *nPublicChats; i++ { - chat := protocol.CreatePublicChat(randomString(10), &testTimeSource{}) - err = wakuext.SaveChat(context.Background(), chat) - if err != nil { - return - } - - } - - logger.Info("Creating one to one chats") - - for i := 0; i < *nOneToOneChats; i++ { - key, err := crypto.GenerateKey() - if err != nil { - return - } - - keyString := common.PubkeyToHex(&key.PublicKey) - chat := protocol.CreateOneToOneChat(keyString, &key.PublicKey, &testTimeSource{}) - err = wakuext.SaveChat(context.Background(), chat) - if err != nil { - return - } - - } - -} - -func getDefaultDataDir() string { - if home := os.Getenv("HOME"); home != "" { - return filepath.Join(home, ".statusd") - } - return "./statusd-data" -} - -func setupLogging(config *params.NodeConfig) { - if *logLevel != "" { - config.LogLevel = *logLevel - } - - colors := !(*logWithoutColors) && terminal.IsTerminal(int(os.Stdin.Fd())) - if err := logutils.OverrideRootLogWithConfig(config, colors); err != nil { - stdlog.Fatalf("Error initializing logger: %v", err) - } -} - -var ( - errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set") - errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set") - errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http") -) - -// printVersion prints verbose output about version and config. -func printVersion(config *params.NodeConfig) { - fmt.Println(strings.Title(config.Name)) - fmt.Println("Version:", config.Version) - fmt.Println("Network ID:", config.NetworkID) - fmt.Println("Go Version:", runtime.Version()) - fmt.Println("OS:", runtime.GOOS) - fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) - fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) - - fmt.Println("Loaded Config: ", config) -} - -func printUsage() { - usage := ` -Usage: statusd [options] -Examples: - statusd # run regular Whisper node that joins Status network - statusd -c ./default.json # run node with configuration specified in ./default.json file - statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file - statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call - -Options: -` - fmt.Fprint(os.Stderr, usage) - flag.PrintDefaults() -} - -const pathWalletRoot = "m/44'/60'/0'/0" -const pathEIP1581 = "m/43'/60'/1581'" -const pathDefaultChat = pathEIP1581 + "/0'/0" -const pathDefaultWallet = pathWalletRoot + "/0" - -var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet} - -func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*accounts.Settings, error) { - chatKeyString := derivedAddresses[pathDefaultChat].PublicKey - - settings := &accounts.Settings{} - settings.KeyUID = generatedAccountInfo.KeyUID - settings.Address = types.HexToAddress(generatedAccountInfo.Address) - settings.WalletRootAddress = types.HexToAddress(derivedAddresses[pathWalletRoot].Address) - - // Set chat key & name - name, err := alias.GenerateFromPublicKeyString(chatKeyString) - if err != nil { - return nil, err - } - settings.Name = name - settings.PublicKey = chatKeyString - - settings.DappsAddress = types.HexToAddress(derivedAddresses[pathDefaultWallet].Address) - settings.EIP1581Address = types.HexToAddress(derivedAddresses[pathEIP1581].Address) - settings.Mnemonic = mnemonic - - signingPhrase, err := buildSigningPhrase() - if err != nil { - return nil, err - } - settings.SigningPhrase = signingPhrase - - settings.SendPushNotifications = true - settings.InstallationID = uuid.New().String() - settings.UseMailservers = true - - settings.PreviewPrivacy = true - settings.Currency = "usd" - settings.ProfilePicturesVisibility = 1 - settings.LinkPreviewRequestEnabled = true - - visibleTokens := make(map[string][]string) - visibleTokens["mainnet"] = []string{"SNT"} - visibleTokensJSON, err := json.Marshal(visibleTokens) - if err != nil { - return nil, err - } - visibleTokenJSONRaw := json.RawMessage(visibleTokensJSON) - settings.WalletVisibleTokens = &visibleTokenJSONRaw - - // TODO: fix this - networks := make([]map[string]string, 0) - networksJSON, err := json.Marshal(networks) - if err != nil { - return nil, err - } - networkRawMessage := json.RawMessage(networksJSON) - settings.Networks = &networkRawMessage - settings.CurrentNetwork = "mainnet_rpc" - - return settings, nil -} - -func defaultNodeConfig(installationID string) (*params.NodeConfig, error) { - // Set mainnet - nodeConfig := ¶ms.NodeConfig{} - nodeConfig.NetworkID = 1 - nodeConfig.LogLevel = "ERROR" - nodeConfig.DataDir = "/ethereum/mainnet_rpc" - nodeConfig.UpstreamConfig = params.UpstreamRPCConfig{ - Enabled: true, - URL: "https://mainnet.infura.io/v3/800c641949d64d768a5070a1b0511938", - } - - nodeConfig.Name = "StatusIM" - nodeConfig.Rendezvous = true - clusterConfig, err := params.LoadClusterConfigFromFleet("eth.prod") - if err != nil { - return nil, err - } - nodeConfig.ClusterConfig = *clusterConfig - - nodeConfig.WalletConfig = params.WalletConfig{Enabled: true} - nodeConfig.LocalNotificationsConfig = params.LocalNotificationsConfig{Enabled: true} - nodeConfig.BrowsersConfig = params.BrowsersConfig{Enabled: true} - nodeConfig.PermissionsConfig = params.PermissionsConfig{Enabled: true} - nodeConfig.MailserversConfig = params.MailserversConfig{Enabled: true} - nodeConfig.EnableNTPSync = true - nodeConfig.WakuConfig = params.WakuConfig{ - Enabled: true, - LightClient: true, - MinimumPoW: 0.000001, - } - - nodeConfig.ShhextConfig = params.ShhextConfig{ - BackupDisabledDataDir: "", - InstallationID: installationID, - MaxMessageDeliveryAttempts: 6, - MailServerConfirmations: true, - VerifyTransactionURL: "", - VerifyENSURL: "", - VerifyENSContractAddress: "", - VerifyTransactionChainID: 1, - DataSyncEnabled: true, - PFSEnabled: true, - } - - // TODO: check topics - - return nodeConfig, nil -} - -func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error { - backend.UpdateRootDataDir("./tmp") - manager := backend.AccountManager() - manager.InitKeystore("./tmp") - err := backend.OpenAccounts() - if err != nil { - return err - } - generator := manager.AccountsGenerator() - generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "") - if err != nil { - return err - } - - derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths) - if err != nil { - return err - } - - _, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths) - if err != nil { - return err - } - - account := multiaccounts.Account{ - KeyUID: generatedAccountInfo.KeyUID, - } - settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase) - if err != nil { - return err - } - - nodeConfig, err := defaultNodeConfig(settings.InstallationID) - if err != nil { - return err - } - - walletDerivedAccount := derivedAddresses[pathDefaultWallet] - walletAccount := accounts.Account{ - PublicKey: types.Hex2Bytes(walletDerivedAccount.PublicKey), - Address: types.HexToAddress(walletDerivedAccount.Address), - Color: "", - Wallet: true, - Path: pathDefaultWallet, - Name: "Ethereum account", - } - - chatDerivedAccount := derivedAddresses[pathDefaultChat] - chatAccount := accounts.Account{ - PublicKey: types.Hex2Bytes(chatDerivedAccount.PublicKey), - Address: types.HexToAddress(chatDerivedAccount.Address), - Name: settings.Name, - Chat: true, - Path: pathDefaultChat, - } - - accounts := []accounts.Account{walletAccount, chatAccount} - return backend.StartNodeWithAccountAndConfig(account, "", *settings, nodeConfig, accounts) -} - -var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - -func randomString(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letterRunes[rand.Intn(len(letterRunes))] - } - return string(b) -} diff --git a/cmd/populate-db/signing_phrase.go b/cmd/populate-db/signing_phrase.go deleted file mode 100644 index 42f45c745..000000000 --- a/cmd/populate-db/signing_phrase.go +++ /dev/null @@ -1,650 +0,0 @@ -package main - -import ( - "crypto/rand" - "math/big" -) - -func buildSigningPhrase() (string, error) { - length := big.NewInt(int64(len(dictionary))) - a, err := rand.Int(rand.Reader, length) - if err != nil { - return "", err - } - b, err := rand.Int(rand.Reader, length) - if err != nil { - return "", err - } - c, err := rand.Int(rand.Reader, length) - if err != nil { - return "", err - } - - return dictionary[a.Int64()] + " " + dictionary[b.Int64()] + " " + dictionary[c.Int64()], nil - -} - -var dictionary = []string{ - "acid", - "alto", - "apse", - "arch", - "area", - "army", - "atom", - "aunt", - "babe", - "baby", - "back", - "bail", - "bait", - "bake", - "ball", - "band", - "bank", - "barn", - "base", - "bass", - "bath", - "bead", - "beak", - "beam", - "bean", - "bear", - "beat", - "beef", - "beer", - "beet", - "bell", - "belt", - "bend", - "bike", - "bill", - "bird", - "bite", - "blow", - "blue", - "boar", - "boat", - "body", - "bolt", - "bomb", - "bone", - "book", - "boot", - "bore", - "boss", - "bowl", - "brow", - "bulb", - "bull", - "burn", - "bush", - "bust", - "cafe", - "cake", - "calf", - "call", - "calm", - "camp", - "cane", - "cape", - "card", - "care", - "carp", - "cart", - "case", - "cash", - "cast", - "cave", - "cell", - "cent", - "chap", - "chef", - "chin", - "chip", - "chop", - "chub", - "chug", - "city", - "clam", - "clef", - "clip", - "club", - "clue", - "coal", - "coat", - "code", - "coil", - "coin", - "coke", - "cold", - "colt", - "comb", - "cone", - "cook", - "cope", - "copy", - "cord", - "cork", - "corn", - "cost", - "crab", - "craw", - "crew", - "crib", - "crop", - "crow", - "curl", - "cyst", - "dame", - "dare", - "dark", - "dart", - "dash", - "data", - "date", - "dead", - "deal", - "dear", - "debt", - "deck", - "deep", - "deer", - "desk", - "dhow", - "diet", - "dill", - "dime", - "dirt", - "dish", - "disk", - "dock", - "doll", - "door", - "dory", - "drag", - "draw", - "drop", - "drug", - "drum", - "duck", - "dump", - "dust", - "duty", - "ease", - "east", - "eave", - "eddy", - "edge", - "envy", - "epee", - "exam", - "exit", - "face", - "fact", - "fail", - "fall", - "fame", - "fang", - "farm", - "fawn", - "fear", - "feed", - "feel", - "feet", - "file", - "fill", - "film", - "find", - "fine", - "fire", - "fish", - "flag", - "flat", - "flax", - "flow", - "foam", - "fold", - "font", - "food", - "foot", - "fork", - "form", - "fort", - "fowl", - "frog", - "fuel", - "full", - "gain", - "gale", - "galn", - "game", - "garb", - "gate", - "gear", - "gene", - "gift", - "girl", - "give", - "glad", - "glen", - "glue", - "glut", - "goal", - "goat", - "gold", - "golf", - "gong", - "good", - "gown", - "grab", - "gram", - "gray", - "grey", - "grip", - "grit", - "gyro", - "hail", - "hair", - "half", - "hall", - "hand", - "hang", - "harm", - "harp", - "hate", - "hawk", - "head", - "heat", - "heel", - "hell", - "helo", - "help", - "hemp", - "herb", - "hide", - "high", - "hill", - "hire", - "hive", - "hold", - "hole", - "home", - "hood", - "hoof", - "hook", - "hope", - "hops", - "horn", - "hose", - "host", - "hour", - "hunt", - "hurt", - "icon", - "idea", - "inch", - "iris", - "iron", - "item", - "jail", - "jeep", - "jeff", - "joey", - "join", - "joke", - "judo", - "jump", - "junk", - "jury", - "jute", - "kale", - "keep", - "kick", - "kill", - "kilt", - "kind", - "king", - "kiss", - "kite", - "knee", - "knot", - "lace", - "lack", - "lady", - "lake", - "lamb", - "lamp", - "land", - "lark", - "lava", - "lawn", - "lead", - "leaf", - "leek", - "lier", - "life", - "lift", - "lily", - "limo", - "line", - "link", - "lion", - "lisa", - "list", - "load", - "loaf", - "loan", - "lock", - "loft", - "long", - "look", - "loss", - "lout", - "love", - "luck", - "lung", - "lute", - "lynx", - "lyre", - "maid", - "mail", - "main", - "make", - "male", - "mall", - "manx", - "many", - "mare", - "mark", - "mask", - "mass", - "mate", - "math", - "meal", - "meat", - "meet", - "menu", - "mess", - "mice", - "midi", - "mile", - "milk", - "mime", - "mind", - "mine", - "mini", - "mint", - "miss", - "mist", - "moat", - "mode", - "mole", - "mood", - "moon", - "most", - "moth", - "move", - "mule", - "mutt", - "nail", - "name", - "neat", - "neck", - "need", - "neon", - "nest", - "news", - "node", - "nose", - "note", - "oboe", - "okra", - "open", - "oval", - "oven", - "oxen", - "pace", - "pack", - "page", - "pail", - "pain", - "pair", - "palm", - "pard", - "park", - "part", - "pass", - "past", - "path", - "peak", - "pear", - "peen", - "peer", - "pelt", - "perp", - "pest", - "pick", - "pier", - "pike", - "pile", - "pimp", - "pine", - "ping", - "pink", - "pint", - "pipe", - "piss", - "pith", - "plan", - "play", - "plot", - "plow", - "poem", - "poet", - "pole", - "polo", - "pond", - "pony", - "poof", - "pool", - "port", - "post", - "prow", - "pull", - "puma", - "pump", - "pupa", - "push", - "quit", - "race", - "rack", - "raft", - "rage", - "rail", - "rain", - "rake", - "rank", - "rate", - "read", - "rear", - "reef", - "rent", - "rest", - "rice", - "rich", - "ride", - "ring", - "rise", - "risk", - "road", - "robe", - "rock", - "role", - "roll", - "roof", - "room", - "root", - "rope", - "rose", - "ruin", - "rule", - "rush", - "ruth", - "sack", - "safe", - "sage", - "sail", - "sale", - "salt", - "sand", - "sari", - "sash", - "save", - "scow", - "seal", - "seat", - "seed", - "self", - "sell", - "shed", - "shin", - "ship", - "shoe", - "shop", - "shot", - "show", - "sick", - "side", - "sign", - "silk", - "sill", - "silo", - "sing", - "sink", - "site", - "size", - "skin", - "sled", - "slip", - "smog", - "snob", - "snow", - "soap", - "sock", - "soda", - "sofa", - "soft", - "soil", - "song", - "soot", - "sort", - "soup", - "spot", - "spur", - "stag", - "star", - "stay", - "stem", - "step", - "stew", - "stop", - "stud", - "suck", - "suit", - "swan", - "swim", - "tail", - "tale", - "talk", - "tank", - "tard", - "task", - "taxi", - "team", - "tear", - "teen", - "tell", - "temp", - "tent", - "term", - "test", - "text", - "thaw", - "tile", - "till", - "time", - "tire", - "toad", - "toga", - "togs", - "tone", - "tool", - "toot", - "tote", - "tour", - "town", - "tram", - "tray", - "tree", - "trim", - "trip", - "tuba", - "tube", - "tuna", - "tune", - "turn", - "tutu", - "twig", - "type", - "unit", - "user", - "vane", - "vase", - "vast", - "veal", - "veil", - "vein", - "vest", - "vibe", - "view", - "vise", - "wait", - "wake", - "walk", - "wall", - "wash", - "wasp", - "wave", - "wear", - "weed", - "week", - "well", - "west", - "whip", - "wife", - "will", - "wind", - "wine", - "wing", - "wire", - "wish", - "wolf", - "wood", - "wool", - "word", - "work", - "worm", - "wrap", - "wren", - "yard", - "yarn", - "yawl", - "year", - "yoga", - "yoke", - "yurt", - "zinc", - "zone", -} diff --git a/cmd/populate-db/sync.go b/cmd/populate-db/sync.go deleted file mode 100644 index 18658a3c8..000000000 --- a/cmd/populate-db/sync.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "context" - "time" - - "github.com/status-im/status-go/node" -) - -func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc) { - if timeout == 0 { - return context.WithCancel(context.Background()) - } - - return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute) -} - -// syncAndStopNode tries to sync the blockchain and stop the node. -// It returns an exit code (`0` if successful or `1` in case of error) -// that can be used in `os.Exit` to exit immediately when the function returns. -// The special exit code `-1` is used if execution was interrupted. -func syncAndStopNode(interruptCh <-chan struct{}, statusNode *node.StatusNode, timeout int) (exitCode int) { - - logger.Info("syncAndStopNode: node will synchronize the chain and exit", "timeoutInMins", timeout) - - ctx, cancel := createContextFromTimeout(timeout) - defer cancel() - - doneSync := make(chan struct{}) - errSync := make(chan error) - go func() { - if err := statusNode.EnsureSync(ctx); err != nil { - errSync <- err - } - close(doneSync) - }() - - select { - case err := <-errSync: - logger.Error("syncAndStopNode: failed to sync the chain", "error", err) - exitCode = 1 - case <-doneSync: - case <-interruptCh: - // cancel context and return immediately if interrupted - // `-1` is used as a special exit code to denote interruption - return -1 - } - - if err := statusNode.Stop(); err != nil { - logger.Error("syncAndStopNode: failed to stop the node", "error", err) - return 1 - } - return -} diff --git a/api/connection.go b/connection/state.go similarity index 68% rename from api/connection.go rename to connection/state.go index 1e8d3bcd2..7035fadba 100644 --- a/api/connection.go +++ b/connection/state.go @@ -1,14 +1,14 @@ -package api +package connection import ( "fmt" ) -// connectionState represents device connection state and type, +// State represents device connection state and type, // as reported by mobile framework. // // Zero value represents default assumption about network (online and unknown type). -type connectionState struct { +type State struct { Offline bool `json:"offline"` Type connectionType `json:"type"` Expensive bool `json:"expensive"` @@ -22,19 +22,19 @@ type connectionState struct { type connectionType byte const ( - offline = "offline" - wifi = "wifi" - cellular = "cellular" - unknown = "unknown" - none = "none" + Offline = "offline" + Wifi = "wifi" + Cellular = "cellular" + Unknown = "unknown" + None = "none" ) -// newConnectionType creates new connectionType from string. -func newConnectionType(s string) connectionType { +// NewConnectionType creates new connectionType from string. +func NewConnectionType(s string) connectionType { switch s { - case cellular: + case Cellular: return connectionCellular - case wifi: + case Wifi: return connectionWifi } @@ -48,20 +48,24 @@ const ( connectionWifi // WIFI or iOS simulator ) +func (c State) IsExpensive() bool { + return c.Expensive || c.Type == connectionCellular +} + // string formats ConnectionState for logs. Implements Stringer. -func (c connectionState) String() string { +func (c State) String() string { if c.Offline { - return offline + return Offline } var typ string switch c.Type { case connectionWifi: - typ = wifi + typ = Wifi case connectionCellular: - typ = cellular + typ = Cellular default: - typ = unknown + typ = Unknown } if c.Expensive { diff --git a/api/connection_test.go b/connection/state_test.go similarity index 63% rename from api/connection_test.go rename to connection/state_test.go index 6e4744d91..fc16e5330 100644 --- a/api/connection_test.go +++ b/connection/state_test.go @@ -1,56 +1,56 @@ -package api +package connection import "testing" func TestConnectionType(t *testing.T) { - c := newConnectionType("wifi") + c := NewConnectionType("wifi") if c != connectionWifi { t.Fatalf("Wrong connection type: %v", c) } - c = newConnectionType("cellular") + c = NewConnectionType("cellular") if c != connectionCellular { t.Fatalf("Wrong connection type: %v", c) } - c = newConnectionType("bluetooth") + c = NewConnectionType("bluetooth") if c != connectionUnknown { t.Fatalf("Wrong connection type: %v", c) } } -func TestConnectionState(t *testing.T) { +func TestState(t *testing.T) { tests := []struct { name string - state connectionState + state State expected string }{ { "zero value", - connectionState{}, + State{}, "unknown", }, { "offline", - connectionState{Offline: true}, + State{Offline: true}, "offline", }, { "wifi", - connectionState{Type: connectionWifi}, + State{Type: connectionWifi}, "wifi", }, { "wifi tethered", - connectionState{Type: connectionWifi, Expensive: true}, + State{Type: connectionWifi, Expensive: true}, "wifi (expensive)", }, { "unknown", - connectionState{Type: connectionUnknown}, + State{Type: connectionUnknown}, "unknown", }, { "cellular", - connectionState{Type: connectionCellular}, + State{Type: connectionCellular}, "cellular", }, } diff --git a/go.mod b/go.mod index 84796b2f0..69f41fd7e 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ replace github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164 require ( github.com/PuerkitoBio/goquery v1.6.0 // indirect github.com/beevik/ntp v0.2.0 - github.com/btcsuite/btcd v0.20.1-beta github.com/cenkalti/backoff/v3 v3.2.2 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v1.7.1 @@ -38,7 +37,6 @@ require ( github.com/mat/besticon v3.12.0+incompatible github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.10 // indirect - github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f github.com/mattn/go-runewidth v0.0.6 // indirect github.com/mattn/go-sqlite3 v1.12.0 // indirect github.com/multiformats/go-multiaddr v0.1.1 @@ -54,7 +52,6 @@ require ( github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.0 - github.com/prometheus/common v0.9.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 @@ -76,7 +73,6 @@ require ( golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd // indirect - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/text v0.3.3 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 // indirect diff --git a/go.sum b/go.sum index bf9066c17..d5e139ef4 100644 --- a/go.sum +++ b/go.sum @@ -24,10 +24,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.0 h1:qDaE0QoF29wKBb3+pXFrJFy1ihe5OT9OiXhg1t85SxM= @@ -441,8 +439,6 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f h1:QTRRO+ozoYgT3CQRIzNVYJRU3DB8HRnkZv6mr4ISmMA= -github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k= @@ -621,8 +617,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/doubleratchet v3.0.0+incompatible h1:aJ1ejcSERpSzmWZBgtfYtiU2nF0Q8ZkGyuEPYETXkCY= github.com/status-im/doubleratchet v3.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU= -github.com/status-im/go-ethereum v1.9.5-status.11 h1:97yCttJkIXoTZGrew7Lkl6uOGaeYELdp7gMnKBm1YwY= -github.com/status-im/go-ethereum v1.9.5-status.11/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g= github.com/status-im/go-ethereum v1.9.5-status.12 h1:+QZE2x5zF1CvNo3V+WaCzeOoar8pdh5Y7BGaAJ83pOw= github.com/status-im/go-ethereum v1.9.5-status.12/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g= github.com/status-im/go-multiaddr-ethv4 v1.2.0 h1:OT84UsUzTCwguqCpJqkrCMiL4VZ1SvUtH9a5MsZupBk= @@ -649,7 +643,6 @@ github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639 github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -779,7 +772,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -858,7 +850,6 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/mailserver/cleaner_test.go b/mailserver/cleaner_test.go index b53be63c6..c885df9ee 100644 --- a/mailserver/cleaner_test.go +++ b/mailserver/cleaner_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/whisper" + waku "github.com/status-im/status-go/waku/common" ) func TestCleaner(t *testing.T) { @@ -88,8 +88,8 @@ func BenchmarkCleanerPruneM100_000_B100(b *testing.B) { benchmarkCleanerPrune(b, 100000, 100) } -func setupTestServer(t *testing.T) *WhisperMailServer { - var s WhisperMailServer +func setupTestServer(t *testing.T) *WakuMailServer { + var s WakuMailServer db, _ := leveldb.Open(storage.NewMemStorage(), nil) s.ms = &mailServer{ @@ -97,13 +97,13 @@ func setupTestServer(t *testing.T) *WhisperMailServer { ldb: db, done: make(chan struct{}), }, - adapter: &whisperAdapter{}, + adapter: &wakuAdapter{}, } s.minRequestPoW = powRequirement return &s } -func archiveEnvelope(t *testing.T, sentTime time.Time, server *WhisperMailServer) *whisper.Envelope { +func archiveEnvelope(t *testing.T, sentTime time.Time, server *WakuMailServer) *waku.Envelope { env, err := generateEnvelope(sentTime) require.NoError(t, err) server.Archive(env) @@ -117,7 +117,7 @@ func testPrune(t *testing.T, u time.Time, expected int, c *dbCleaner) { require.Equal(t, expected, n) } -func testMessagesCount(t *testing.T, expected int, s *WhisperMailServer) { +func testMessagesCount(t *testing.T, expected int, s *WakuMailServer) { count := countMessages(t, s.ms.db) require.Equal(t, expected, count, fmt.Sprintf("expected %d message, got: %d", expected, count)) } @@ -142,7 +142,7 @@ func countMessages(t *testing.T, db DB) int { defer func() { _ = i.Release() }() for i.Next() { - var env whisper.Envelope + var env waku.Envelope value, err := i.GetEnvelope(query.bloom) if err != nil { t.Fatal(err) diff --git a/mailserver/mailserver.go b/mailserver/mailserver.go index adf1ba3f5..c751047df 100644 --- a/mailserver/mailserver.go +++ b/mailserver/mailserver.go @@ -18,6 +18,7 @@ package mailserver import ( "crypto/ecdsa" + "encoding/binary" "errors" "fmt" "math/rand" @@ -155,6 +156,72 @@ func (s *WakuMailServer) DeliverMail(peerID []byte, req *wakucommon.Envelope) { s.ms.DeliverMail(types.BytesToHash(peerID), types.Hash(req.Hash()), payload) } +// bloomFromReceivedMessage for a given whisper.ReceivedMessage it extracts the +// used bloom filter. +func (s *WakuMailServer) bloomFromReceivedMessage(msg *wakucommon.ReceivedMessage) ([]byte, error) { + payloadSize := len(msg.Payload) + + if payloadSize < 8 { + return nil, errors.New("Undersized p2p request") + } else if payloadSize == 8 { + return wakucommon.MakeFullNodeBloom(), nil + } else if payloadSize < 8+wakucommon.BloomFilterSize { + return nil, errors.New("Undersized bloom filter in p2p request") + } + + return msg.Payload[8 : 8+wakucommon.BloomFilterSize], nil +} + +func (s *WakuMailServer) decompositeRequest(peerID []byte, request *wakucommon.Envelope) (MessagesRequestPayload, error) { + var ( + payload MessagesRequestPayload + err error + ) + + if s.minRequestPoW > 0.0 && request.PoW() < s.minRequestPoW { + return payload, fmt.Errorf("PoW() is too low") + } + + decrypted := s.openEnvelope(request) + if decrypted == nil { + return payload, fmt.Errorf("failed to decrypt p2p request") + } + + if err := checkMsgSignature(decrypted.Src, peerID); err != nil { + return payload, err + } + + payload.Bloom, err = s.bloomFromReceivedMessage(decrypted) + if err != nil { + return payload, err + } + + payload.Lower = binary.BigEndian.Uint32(decrypted.Payload[:4]) + payload.Upper = binary.BigEndian.Uint32(decrypted.Payload[4:8]) + + if payload.Upper < payload.Lower { + err := fmt.Errorf("query range is invalid: from > to (%d > %d)", payload.Lower, payload.Upper) + return payload, err + } + + lowerTime := time.Unix(int64(payload.Lower), 0) + upperTime := time.Unix(int64(payload.Upper), 0) + if upperTime.Sub(lowerTime) > maxQueryRange { + err := fmt.Errorf("query range too big for peer %s", string(peerID)) + return payload, err + } + + if len(decrypted.Payload) >= requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength { + payload.Limit = binary.BigEndian.Uint32(decrypted.Payload[requestTimeRangeLength+wakucommon.BloomFilterSize:]) + } + + if len(decrypted.Payload) == requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength+DBKeyLength { + payload.Cursor = decrypted.Payload[requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength:] + } + + return payload, nil +} + func (s *WakuMailServer) setupDecryptor(password, asymKey string) error { s.symFilter = nil s.asymFilter = nil diff --git a/mailserver/mailserver_db_postgres_test.go b/mailserver/mailserver_db_postgres_test.go index 79c05cbc4..e73cc383f 100644 --- a/mailserver/mailserver_db_postgres_test.go +++ b/mailserver/mailserver_db_postgres_test.go @@ -18,7 +18,7 @@ import ( gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/whisper" + waku "github.com/status-im/status-go/waku/common" ) func TestPostgresDB_BuildIteratorWithBloomFilter(t *testing.T) { @@ -44,10 +44,10 @@ func TestPostgresDB_BuildIteratorWithBloomFilter(t *testing.T) { rawValue, err := iter.GetEnvelope(nil) require.NoError(t, err) require.NotEmpty(t, rawValue) - var receivedEnvelope whisper.Envelope + var receivedEnvelope waku.Envelope err = rlp.DecodeBytes(rawValue, &receivedEnvelope) require.NoError(t, err) - require.EqualValues(t, whisper.BytesToTopic(topic), receivedEnvelope.Topic) + require.EqualValues(t, waku.BytesToTopic(topic), receivedEnvelope.Topic) err = iter.Release() require.NoError(t, err) @@ -77,10 +77,10 @@ func TestPostgresDB_BuildIteratorWithTopic(t *testing.T) { rawValue, err := iter.GetEnvelope(nil) require.NoError(t, err) require.NotEmpty(t, rawValue) - var receivedEnvelope whisper.Envelope + var receivedEnvelope waku.Envelope err = rlp.DecodeBytes(rawValue, &receivedEnvelope) require.NoError(t, err) - require.EqualValues(t, whisper.BytesToTopic(topic), receivedEnvelope.Topic) + require.EqualValues(t, waku.BytesToTopic(topic), receivedEnvelope.Topic) err = iter.Release() require.NoError(t, err) @@ -92,15 +92,15 @@ func newTestEnvelope(topic []byte) (types.Envelope, error) { if err != nil { return nil, err } - params := whisper.MessageParams{ + params := waku.MessageParams{ TTL: 10, PoW: 2.0, Payload: []byte("hello world"), WorkTime: 1, - Topic: whisper.BytesToTopic(topic), + Topic: waku.BytesToTopic(topic), Dst: &privateKey.PublicKey, } - message, err := whisper.NewSentMessage(¶ms) + message, err := waku.NewSentMessage(¶ms) if err != nil { return nil, err } diff --git a/mailserver/mailserver_test.go b/mailserver/mailserver_test.go index c2b553298..16c661118 100644 --- a/mailserver/mailserver_test.go +++ b/mailserver/mailserver_test.go @@ -19,7 +19,6 @@ package mailserver import ( "crypto/ecdsa" "encoding/binary" - "encoding/hex" "errors" "fmt" "io/ioutil" @@ -35,7 +34,8 @@ import ( "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/params" - "github.com/status-im/status-go/whisper" + waku "github.com/status-im/status-go/waku" + wakucommon "github.com/status-im/status-go/waku/common" ) const powRequirement = 0.00001 @@ -59,28 +59,23 @@ func TestMailserverSuite(t *testing.T) { type MailserverSuite struct { suite.Suite - server *WhisperMailServer - shh *whisper.Whisper - config *params.WhisperConfig + server *WakuMailServer + shh *waku.Waku + config *params.WakuConfig dataDir string } func (s *MailserverSuite) SetupTest() { - s.server = &WhisperMailServer{} - s.shh = whisper.New(&whisper.DefaultConfig) + s.server = &WakuMailServer{} + s.shh = waku.New(&waku.DefaultConfig, nil) s.shh.RegisterMailServer(s.server) tmpDir, err := ioutil.TempDir("", "mailserver-test") s.Require().NoError(err) s.dataDir = tmpDir - // required files to validate mail server decryption method - privateKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - s.config = ¶ms.WhisperConfig{ + s.config = ¶ms.WakuConfig{ DataDir: tmpDir, - MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(privateKey)), MailServerPassword: "testpassword", } } @@ -90,30 +85,18 @@ func (s *MailserverSuite) TearDownTest() { } func (s *MailserverSuite) TestInit() { - asymKey, err := crypto.GenerateKey() - s.Require().NoError(err) - testCases := []struct { - config params.WhisperConfig + config params.WakuConfig expectedError error info string }{ { - config: params.WhisperConfig{DataDir: ""}, + config: params.WakuConfig{DataDir: ""}, expectedError: errDirectoryNotProvided, info: "config with empty DataDir", }, { - config: params.WhisperConfig{ - DataDir: s.config.DataDir, - MailServerPassword: "", - MailServerAsymKey: "", - }, - expectedError: errDecryptionMethodNotProvided, - info: "config with an empty password and empty asym key", - }, - { - config: params.WhisperConfig{ + config: params.WakuConfig{ DataDir: s.config.DataDir, MailServerPassword: "pwd", }, @@ -121,24 +104,7 @@ func (s *MailserverSuite) TestInit() { info: "config with correct DataDir and Password", }, { - config: params.WhisperConfig{ - DataDir: s.config.DataDir, - MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)), - }, - expectedError: nil, - info: "config with correct DataDir and AsymKey", - }, - { - config: params.WhisperConfig{ - DataDir: s.config.DataDir, - MailServerAsymKey: hex.EncodeToString(crypto.FromECDSA(asymKey)), - MailServerPassword: "pwd", - }, - expectedError: nil, - info: "config with both asym key and password", - }, - { - config: params.WhisperConfig{ + config: params.WakuConfig{ DataDir: s.config.DataDir, MailServerPassword: "pwd", MailServerRateLimit: 5, @@ -150,13 +116,15 @@ func (s *MailserverSuite) TestInit() { for _, tc := range testCases { s.T().Run(tc.info, func(*testing.T) { - mailServer := &WhisperMailServer{} - shh := whisper.New(&whisper.DefaultConfig) + mailServer := &WakuMailServer{} + shh := waku.New(&waku.DefaultConfig, nil) shh.RegisterMailServer(mailServer) err := mailServer.Init(shh, &tc.config) - s.Equal(tc.expectedError, err) - defer mailServer.Close() + s.Require().Equal(tc.expectedError, err) + if err == nil { + defer mailServer.Close() + } // db should be open only if there was no error if tc.expectedError == nil { @@ -172,81 +140,8 @@ func (s *MailserverSuite) TestInit() { } } -func (s *MailserverSuite) TestSetupRequestMessageDecryptor() { - // without configured Password and AsymKey - config := *s.config - config.MailServerAsymKey = "" - config.MailServerPassword = "" - s.Error(errDecryptionMethodNotProvided, s.server.Init(s.shh, &config)) - - // Password should work ok - config = *s.config - config.MailServerAsymKey = "" // clear asym key field - s.NoError(s.server.Init(s.shh, &config)) - s.Require().NotNil(s.server.symFilter) - s.NotNil(s.server.symFilter.KeySym) - s.Nil(s.server.asymFilter) - s.server.Close() - - // AsymKey can also be used - config = *s.config - config.MailServerPassword = "" // clear password field - s.NoError(s.server.Init(s.shh, &config)) - s.Nil(s.server.symFilter) // important: symmetric filter should be nil - s.Require().NotNil(s.server.asymFilter) - s.Equal(config.MailServerAsymKey, hex.EncodeToString(crypto.FromECDSA(s.server.asymFilter.KeyAsym))) - s.server.Close() - - // when Password and AsymKey are set, both are supported - config = *s.config - s.NoError(s.server.Init(s.shh, &config)) - s.Require().NotNil(s.server.symFilter) - s.NotNil(s.server.symFilter.KeySym) - s.NotNil(s.server.asymFilter.KeyAsym) - s.server.Close() -} - -func (s *MailserverSuite) TestOpenEnvelopeWithSymKey() { - // Setup the server with a sym key - config := *s.config - config.MailServerAsymKey = "" // clear asym key - s.NoError(s.server.Init(s.shh, &config)) - - // Prepare a valid envelope - s.Require().NotNil(s.server.symFilter) - symKey := s.server.symFilter.KeySym - env, err := generateEnvelopeWithKeys(time.Now(), symKey, nil) - s.Require().NoError(err) - - // Test openEnvelope with a valid envelope - d := s.server.openEnvelope(env) - s.NotNil(d) - s.Equal(testPayload, d.Payload) - s.server.Close() -} - -func (s *MailserverSuite) TestOpenEnvelopeWithAsymKey() { - // Setup the server with an asymmetric key - config := *s.config - config.MailServerPassword = "" // clear password field - s.NoError(s.server.Init(s.shh, &config)) - - // Prepare a valid envelope - s.Require().NotNil(s.server.asymFilter) - pubKey := s.server.asymFilter.KeyAsym.PublicKey - env, err := generateEnvelopeWithKeys(time.Now(), nil, &pubKey) - s.Require().NoError(err) - - // Test openEnvelope with a valid asymmetric key - d := s.server.openEnvelope(env) - s.NotNil(d) - s.Equal(testPayload, d.Payload) - s.server.Close() -} - func (s *MailserverSuite) TestArchive() { config := *s.config - config.MailServerAsymKey = "" // clear asym key err := s.server.Init(s.shh, &config) s.Require().NoError(err) @@ -294,7 +189,7 @@ func (s *MailserverSuite) TestRequestPaginationLimit() { defer s.server.Close() var ( - sentEnvelopes []*whisper.Envelope + sentEnvelopes []*wakucommon.Envelope sentHashes []common.Hash receivedHashes []common.Hash archiveKeys []string @@ -490,7 +385,7 @@ func (s *MailserverSuite) TestProcessRequestDeadlockHandling() { s.setupServer(s.server) defer s.server.Close() - var archievedEnvelopes []*whisper.Envelope + var archievedEnvelopes []*wakucommon.Envelope now := time.Now() count := uint32(10) @@ -584,7 +479,7 @@ func (s *MailserverSuite) TestProcessRequestDeadlockHandling() { } } -func (s *MailserverSuite) messageExists(envelope *whisper.Envelope, low, upp uint32, bloom []byte, limit uint32) bool { +func (s *MailserverSuite) messageExists(envelope *wakucommon.Envelope, low, upp uint32, bloom []byte, limit uint32) bool { receivedHashes, _, _ := processRequestAndCollectHashes(s.server, MessagesRequestPayload{ Lower: low, Upper: upp, @@ -599,49 +494,13 @@ func (s *MailserverSuite) messageExists(envelope *whisper.Envelope, low, upp uin return false } -func (s *MailserverSuite) TestBloomFromReceivedMessage() { - testCases := []struct { - msg whisper.ReceivedMessage - expectedBloom []byte - expectedErr error - info string - }{ - { - msg: whisper.ReceivedMessage{}, - expectedBloom: []byte(nil), - expectedErr: errors.New("Undersized p2p request"), - info: "getting bloom filter for an empty whisper message should produce an error", - }, - { - msg: whisper.ReceivedMessage{Payload: []byte("hohohohoho")}, - expectedBloom: []byte(nil), - expectedErr: errors.New("Undersized bloom filter in p2p request"), - info: "getting bloom filter for a malformed whisper message should produce an error", - }, - { - msg: whisper.ReceivedMessage{Payload: []byte("12345678")}, - expectedBloom: whisper.MakeFullNodeBloom(), - expectedErr: nil, - info: "getting bloom filter for a valid whisper message should be successful", - }, - } - - for _, tc := range testCases { - s.T().Run(tc.info, func(*testing.T) { - bloom, err := s.server.bloomFromReceivedMessage(&tc.msg) - s.Equal(tc.expectedErr, err) - s.Equal(tc.expectedBloom, bloom) - }) - } -} - -func (s *MailserverSuite) setupServer(server *WhisperMailServer) { +func (s *MailserverSuite) setupServer(server *WakuMailServer) { const password = "password_for_this_test" - s.shh = whisper.New(&whisper.DefaultConfig) + s.shh = waku.New(&waku.DefaultConfig, nil) s.shh.RegisterMailServer(server) - err := server.Init(s.shh, ¶ms.WhisperConfig{ + err := server.Init(s.shh, ¶ms.WakuConfig{ DataDir: s.dataDir, MailServerPassword: password, MinimumPoW: powRequirement, @@ -656,8 +515,8 @@ func (s *MailserverSuite) setupServer(server *WhisperMailServer) { } } -func (s *MailserverSuite) prepareRequest(envelopes []*whisper.Envelope, limit uint32) ( - []byte, *whisper.Envelope, error, +func (s *MailserverSuite) prepareRequest(envelopes []*wakucommon.Envelope, limit uint32) ( + []byte, *wakucommon.Envelope, error, ) { if len(envelopes) == 0 { return nil, nil, errors.New("envelopes is empty") @@ -676,7 +535,7 @@ func (s *MailserverSuite) prepareRequest(envelopes []*whisper.Envelope, limit ui return peerID, request, nil } -func (s *MailserverSuite) defaultServerParams(env *whisper.Envelope) *ServerTestParams { +func (s *MailserverSuite) defaultServerParams(env *wakucommon.Envelope) *ServerTestParams { id, err := s.shh.NewKeyPair() if err != nil { s.T().Fatalf("failed to generate new key pair with seed %d: %s.", seed, err) @@ -697,7 +556,7 @@ func (s *MailserverSuite) defaultServerParams(env *whisper.Envelope) *ServerTest } } -func (s *MailserverSuite) createRequest(p *ServerTestParams) *whisper.Envelope { +func (s *MailserverSuite) createRequest(p *ServerTestParams) *wakucommon.Envelope { bloom := types.TopicToBloom(p.topic) data := make([]byte, 8) binary.BigEndian.PutUint32(data, p.low) @@ -713,22 +572,22 @@ func (s *MailserverSuite) createRequest(p *ServerTestParams) *whisper.Envelope { return s.createEnvelope(p.topic, data, p.key) } -func (s *MailserverSuite) createEnvelope(topic types.TopicType, data []byte, srcKey *ecdsa.PrivateKey) *whisper.Envelope { +func (s *MailserverSuite) createEnvelope(topic types.TopicType, data []byte, srcKey *ecdsa.PrivateKey) *wakucommon.Envelope { key, err := s.shh.GetSymKey(keyID) if err != nil { s.T().Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err) } - params := &whisper.MessageParams{ + params := &wakucommon.MessageParams{ KeySym: key, - Topic: whisper.TopicType(topic), + Topic: wakucommon.TopicType(topic), Payload: data, PoW: powRequirement * 2, WorkTime: 2, Src: srcKey, } - msg, err := whisper.NewSentMessage(params) + msg, err := wakucommon.NewSentMessage(params) if err != nil { s.T().Fatalf("failed to create new message with seed %d: %s.", seed, err) } @@ -740,9 +599,9 @@ func (s *MailserverSuite) createEnvelope(topic types.TopicType, data []byte, src return env } -func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa.PublicKey) (*whisper.Envelope, error) { - params := &whisper.MessageParams{ - Topic: whisper.TopicType{0x1F, 0x7E, 0xA1, 0x7F}, +func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa.PublicKey) (*wakucommon.Envelope, error) { + params := &wakucommon.MessageParams{ + Topic: wakucommon.TopicType{0x1F, 0x7E, 0xA1, 0x7F}, Payload: testPayload, PoW: powRequirement, WorkTime: 2, @@ -754,7 +613,7 @@ func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa. params.Dst = keyAsym } - msg, err := whisper.NewSentMessage(params) + msg, err := wakucommon.NewSentMessage(params) if err != nil { return nil, fmt.Errorf("failed to create new message with seed %d: %s", seed, err) } @@ -766,12 +625,12 @@ func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa. return env, nil } -func generateEnvelope(sentTime time.Time) (*whisper.Envelope, error) { +func generateEnvelope(sentTime time.Time) (*wakucommon.Envelope, error) { h := crypto.Keccak256Hash([]byte("test sample data")) return generateEnvelopeWithKeys(sentTime, h[:], nil) } -func processRequestAndCollectHashes(server *WhisperMailServer, payload MessagesRequestPayload) ([]common.Hash, []byte, types.Hash) { +func processRequestAndCollectHashes(server *WakuMailServer, payload MessagesRequestPayload) ([]common.Hash, []byte, types.Hash) { iter, _ := server.ms.createIterator(payload) defer func() { _ = iter.Release() }() bundles := make(chan []rlp.RawValue, 10) @@ -781,7 +640,7 @@ func processRequestAndCollectHashes(server *WhisperMailServer, payload MessagesR go func() { for bundle := range bundles { for _, rawEnvelope := range bundle { - var env *whisper.Envelope + var env *wakucommon.Envelope if err := rlp.DecodeBytes(rawEnvelope, &env); err != nil { panic(err) } diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index f21b2eb5d..a9188ba97 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -555,7 +555,7 @@ func (db *Database) GetNotificationsEnabled() (bool, error) { return result, err } -func (db *Database) GetUseMailservers() (bool, error) { +func (db *Database) CanUseMailservers() (bool, error) { var result bool err := db.db.QueryRow("SELECT use_mailservers FROM settings WHERE synthetic_id = 'id'").Scan(&result) if err == sql.ErrNoRows { @@ -564,6 +564,15 @@ func (db *Database) GetUseMailservers() (bool, error) { return result, err } +func (db *Database) CanSyncOnMobileNetwork() (bool, error) { + var result bool + err := db.db.QueryRow("SELECT syncing_on_mobile_network FROM settings WHERE synthetic_id = 'id'").Scan(&result) + if err == sql.ErrNoRows { + return result, nil + } + return result, err +} + func (db *Database) GetMessagesFromContactsOnly() (bool, error) { var result bool err := db.db.QueryRow("SELECT messages_from_contacts_only FROM settings WHERE synthetic_id = 'id'").Scan(&result) diff --git a/node/get_status_node.go b/node/get_status_node.go index 599601d85..9b07e48cb 100644 --- a/node/get_status_node.go +++ b/node/get_status_node.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/status-im/status-go/connection" "github.com/status-im/status-go/db" "github.com/status-im/status-go/discovery" "github.com/status-im/status-go/params" @@ -596,6 +597,16 @@ func (n *StatusNode) WakuExtService() (s *wakuext.Service, err error) { return } +func (n *StatusNode) ConnectionChanged(state connection.State) error { + service, err := n.WakuExtService() + if err != nil { + return err + } + + service.ConnectionChanged(state) + return nil +} + // WalletService returns wallet.Service instance if it was started. func (n *StatusNode) WalletService() (s *wallet.Service, err error) { n.mu.RLock() diff --git a/node/geth_node_api_test.go b/node/geth_node_api_test.go index a24554fbf..44e51b9d8 100644 --- a/node/geth_node_api_test.go +++ b/node/geth_node_api_test.go @@ -8,13 +8,13 @@ import ( "github.com/stretchr/testify/require" "github.com/status-im/status-go/params" - "github.com/status-im/status-go/whisper" + "github.com/status-im/status-go/waku" ) -func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) { +func TestWakuLightModeEnabledSetsEmptyBloomFilter(t *testing.T) { config := params.NodeConfig{ EnableNTPSync: true, - WhisperConfig: params.WhisperConfig{ + WakuConfig: params.WakuConfig{ Enabled: true, LightClient: true, }, @@ -25,19 +25,19 @@ func TestWhisperLightModeEnabledSetsEmptyBloomFilter(t *testing.T) { require.NoError(t, node.Stop()) }() - var whisper *whisper.Whisper - require.NoError(t, node.gethService(&whisper)) + var waku *waku.Waku + require.NoError(t, node.gethService(&waku)) - bloomFilter := whisper.BloomFilter() + bloomFilter := waku.BloomFilter() expectedEmptyBloomFilter := make([]byte, 64) require.NotNil(t, bloomFilter) require.Equal(t, expectedEmptyBloomFilter, bloomFilter) } -func TestWhisperLightModeEnabledSetsNilBloomFilter(t *testing.T) { +func TestWakuLightModeEnabledSetsNilBloomFilter(t *testing.T) { config := params.NodeConfig{ EnableNTPSync: true, - WhisperConfig: params.WhisperConfig{ + WakuConfig: params.WakuConfig{ Enabled: true, LightClient: false, }, @@ -48,78 +48,7 @@ func TestWhisperLightModeEnabledSetsNilBloomFilter(t *testing.T) { require.NoError(t, node.Stop()) }() - var whisper *whisper.Whisper - require.NoError(t, node.gethService(&whisper)) - require.Nil(t, whisper.BloomFilter()) -} - -func TestBridgeSetup(t *testing.T) { - testCases := []struct { - Name string - Skip string - Cfg params.NodeConfig - ErrorMessage string - }{ - { - Name: "no whisper and waku", - Cfg: params.NodeConfig{ - BridgeConfig: params.BridgeConfig{Enabled: true}, - }, - ErrorMessage: "setup bridge: failed to get Whisper: unknown service", - }, - { - Name: "only whisper", - Cfg: params.NodeConfig{ - WhisperConfig: params.WhisperConfig{ - Enabled: true, - LightClient: false, - }, - BridgeConfig: params.BridgeConfig{Enabled: true}, - }, - ErrorMessage: "setup bridge: failed to get Waku: unknown service", - }, - { - Name: "only waku", - Cfg: params.NodeConfig{ - WakuConfig: params.WakuConfig{ - Enabled: true, - LightClient: false, - }, - BridgeConfig: params.BridgeConfig{Enabled: true}, - }, - ErrorMessage: "setup bridge: failed to get Whisper: unknown service", - }, - { - Name: "both", - Skip: "This test is flaky, setting it as skip for now", - Cfg: params.NodeConfig{ - WhisperConfig: params.WhisperConfig{ - Enabled: true, - LightClient: false, - }, - WakuConfig: params.WakuConfig{ - Enabled: true, - LightClient: false, - }, - BridgeConfig: params.BridgeConfig{Enabled: true}, - }, - }, - } - - for _, tc := range testCases { - if tc.Skip != "" { - t.Skip(tc.Skip) - continue - } - t.Run(tc.Name, func(t *testing.T) { - node := New() - err := node.Start(&tc.Cfg, &accounts.Manager{}) - if err != nil { - require.EqualError(t, err, tc.ErrorMessage) - } else if tc.ErrorMessage != "" { - t.Fatalf("expected an error: %s", tc.ErrorMessage) - } - require.NoError(t, node.Stop()) - }) - } + var waku *waku.Waku + require.NoError(t, node.gethService(&waku)) + require.Nil(t, waku.BloomFilter()) } diff --git a/node/geth_status_node_test.go b/node/geth_status_node_test.go index a6d9e5d9c..91300f5ed 100644 --- a/node/geth_status_node_test.go +++ b/node/geth_status_node_test.go @@ -17,7 +17,7 @@ import ( gethnode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/status-im/status-go/whisper" + "github.com/status-im/status-go/waku" "github.com/stretchr/testify/require" @@ -90,7 +90,7 @@ func TestStatusNodeWithDataDir(t *testing.T) { func TestStatusNodeServiceGetters(t *testing.T) { config := params.NodeConfig{ EnableNTPSync: true, - WhisperConfig: params.WhisperConfig{ + WakuConfig: params.WakuConfig{ Enabled: true, }, LightEthConfig: params.LightEthConfig{ @@ -110,9 +110,9 @@ func TestStatusNodeServiceGetters(t *testing.T) { }{ { getter: func() (interface{}, error) { - return n.WhisperService() + return n.WakuService() }, - typ: reflect.TypeOf(&whisper.Whisper{}), + typ: reflect.TypeOf(&waku.Waku{}), }, { getter: func() (interface{}, error) { diff --git a/params/config_test.go b/params/config_test.go index bd23dd0a6..c4cdfb569 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -12,8 +12,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/status-im/status-go/params" "github.com/status-im/status-go/t/utils" ) @@ -31,11 +29,10 @@ func TestNewNodeConfigWithDefaults(t *testing.T) { assert.Equal(t, "/some/data/path/keystore", c.KeyStoreDir) assert.Equal(t, true, c.EnableNTPSync) // assert Whisper - assert.Equal(t, true, c.WhisperConfig.Enabled) - assert.Equal(t, "/some/data/path/wnode", c.WhisperConfig.DataDir) + assert.Equal(t, true, c.WakuConfig.Enabled) + assert.Equal(t, "/some/data/path/waku", c.WakuConfig.DataDir) // assert MailServer - assert.Equal(t, true, c.WhisperConfig.EnableMailServer) - assert.NotEmpty(t, c.WhisperConfig.MailServerPassword) + assert.Equal(t, false, c.WakuConfig.EnableMailServer) // assert cluster assert.Equal(t, false, c.NoDiscovery) assert.Equal(t, params.FleetProd, c.ClusterConfig.Fleet) @@ -45,9 +42,6 @@ func TestNewNodeConfigWithDefaults(t *testing.T) { assert.NotEmpty(t, c.ClusterConfig.PushNotificationsServers) // assert LES assert.Equal(t, true, c.LightEthConfig.Enabled) - // assert peers limits - assert.Contains(t, c.RequireTopics, params.WhisperDiscv5Topic) - assert.Contains(t, c.RequireTopics, discv5.Topic(params.LesTopic(int(c.NetworkID)))) // assert other assert.Equal(t, false, c.HTTPEnabled) assert.Equal(t, false, c.IPCEnabled) @@ -216,104 +210,6 @@ func TestNodeConfigValidate(t *testing.T) { }`, Error: "Rendezvous is enabled, but ClusterConfig.RendezvousNodes is empty", }, - { - Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "MailserverPassword": "foo" - } - }`, - Error: "WhisperConfig.DataDir must be specified when WhisperConfig.EnableMailServer is true", - }, - { - Name: "Validate that WhisperConfig.DataDir is checked to not be empty if mailserver is enabled", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "/other/dir", - "MailserverPassword": "foo" - } - }`, - Error: "WhisperConfig.DataDir must start with DataDir fragment", - }, - { - Name: "Validate that check for WhisperConfig.DataDir passes if it is not empty and mailserver is enabled", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "/some/dir", - "MailserverPassword": "foo" - } - }`, - CheckFunc: func(t *testing.T, config *params.NodeConfig) { - require.Equal(t, "foo", config.WhisperConfig.MailServerPassword) - }, - }, - { - Name: "Validate that WhisperConfig.MailserverPassword and WhisperConfig.MailServerAsymKey are checked to not be empty if mailserver is enabled", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "/some/dir" - } - }`, - Error: "WhisperConfig.MailServerPassword or WhisperConfig.MailServerAsymKey must be specified when WhisperConfig.EnableMailServer is true", - }, - { - Name: "Validate that WhisperConfig.MailServerAsymKey is checked to not be empty if mailserver is enabled", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "/some/dir", - "MailServerAsymKey": "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90" - } - }`, - CheckFunc: func(t *testing.T, config *params.NodeConfig) { - require.Equal(t, "06c365919f1fc8e13ff79a84f1dd14b7e45b869aa5fc0e34940481ee20d32f90", config.WhisperConfig.MailServerAsymKey) - }, - }, - { - Name: "Validate that WhisperConfig.MailServerAsymKey is checked for validity", - Config: `{ - "NetworkId": 1, - "DataDir": "/some/dir", - "KeyStoreDir": "/some/dir", - "NoDiscovery": true, - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "/foo", - "MailServerAsymKey": "bar" - } - }`, - Error: "WhisperConfig.MailServerAsymKey is invalid", - }, { Name: "Validate that PFSEnabled & InstallationID are checked for validity", Config: `{ @@ -321,7 +217,7 @@ func TestNodeConfigValidate(t *testing.T) { "DataDir": "/some/dir", "KeyStoreDir": "/some/dir", "NoDiscovery": true, - "WhisperConfig": { + "WakuConfig": { "Enabled": true, "DataDir": "/foo" }, diff --git a/peers/peerpool_test.go b/peers/peerpool_test.go index 25833f277..75dd45bfd 100644 --- a/peers/peerpool_test.go +++ b/peers/peerpool_test.go @@ -22,7 +22,6 @@ import ( "github.com/status-im/status-go/discovery" "github.com/status-im/status-go/params" "github.com/status-im/status-go/signal" - "github.com/status-im/status-go/whisper" ) type PeerPoolSimulationSuite struct { @@ -62,7 +61,6 @@ func (s *PeerPoolSimulationSuite) SetupTest() { s.discovery = make([]discovery.Discovery, 3) for i := range s.peers { key, _ := crypto.GenerateKey() - whisper := whisper.New(nil) peer := &p2p.Server{ Config: p2p.Config{ MaxPeers: 10, @@ -71,7 +69,6 @@ func (s *PeerPoolSimulationSuite) SetupTest() { PrivateKey: key, NoDiscovery: true, BootstrapNodesV5: []*discv5.Node{bootnodeV5}, - Protocols: whisper.Protocols(), }, } s.NoError(peer.Start()) diff --git a/peers/topicpool_test.go b/peers/topicpool_test.go index 019f1c77e..5b9374990 100644 --- a/peers/topicpool_test.go +++ b/peers/topicpool_test.go @@ -16,7 +16,7 @@ import ( "github.com/status-im/status-go/params" "github.com/status-im/status-go/t/helpers" - "github.com/status-im/status-go/whisper" + "github.com/status-im/status-go/waku" ) type TopicPoolSuite struct { @@ -467,7 +467,7 @@ func TestServerIgnoresInboundPeer(t *testing.T) { topicPool.running = 1 topicPool.maxCachedPeers = 0 - whisper := whisper.New(nil) + waku := waku.New(&waku.DefaultConfig, nil) srvkey, err := crypto.GenerateKey() require.NoError(t, err) server := &p2p.Server{ @@ -477,7 +477,7 @@ func TestServerIgnoresInboundPeer(t *testing.T) { ListenAddr: ":0", PrivateKey: srvkey, NoDiscovery: true, - Protocols: whisper.Protocols(), + Protocols: waku.Protocols(), }, } require.NoError(t, server.Start()) @@ -490,7 +490,7 @@ func TestServerIgnoresInboundPeer(t *testing.T) { ListenAddr: ":0", PrivateKey: clientkey, NoDiscovery: true, - Protocols: whisper.Protocols(), + Protocols: waku.Protocols(), }, } require.NoError(t, client.Start()) diff --git a/protocol/chat.go b/protocol/chat.go index cb38e26c4..f89b6a2a8 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -292,6 +292,7 @@ func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource comm Name: name, Timestamp: int64(timesource.GetCurrentTime()), Active: true, + Joined: int64(timesource.GetCurrentTime()), ChatType: ChatTypeOneToOne, } } @@ -309,6 +310,7 @@ func CreateCommunityChat(orgID, chatID string, orgChat *protobuf.CommunityChat, Color: color, ID: orgID + chatID, Timestamp: int64(timesource.GetCurrentTime()), + Joined: int64(timesource.GetCurrentTime()), ChatType: ChatTypeCommunityChat, } } @@ -348,6 +350,7 @@ func CreatePublicChat(name string, timesource common.TimeSource) *Chat { Name: name, Active: true, Timestamp: int64(timesource.GetCurrentTime()), + Joined: int64(timesource.GetCurrentTime()), Color: chatColors[rand.Intn(len(chatColors))], // nolint: gosec ChatType: ChatTypePublic, } @@ -365,6 +368,7 @@ func CreateProfileChat(pubkey string, timesource common.TimeSource) *Chat { Name: id, Active: true, Timestamp: int64(timesource.GetCurrentTime()), + Joined: int64(timesource.GetCurrentTime()), Color: chatColors[rand.Intn(len(chatColors))], // nolint: gosec ChatType: ChatTypeProfile, Profile: pubkey, diff --git a/protocol/communities_messenger_test.go b/protocol/communities_messenger_test.go index c2402d071..cabb6d610 100644 --- a/protocol/communities_messenger_test.go +++ b/protocol/communities_messenger_test.go @@ -420,18 +420,6 @@ func (s *MessengerCommunitiesSuite) TestPostToCommunityChat() { s.Require().Len(response.Communities(), 1) s.Require().True(response.Communities()[0].Joined()) s.Require().Len(response.Chats(), 1) - s.Require().Len(response.Filters, 2) - - var orgFilterFound bool - var chatFilterFound bool - for _, f := range response.Filters { - orgFilterFound = orgFilterFound || f.ChatID == response.Communities()[0].IDString() - chatFilterFound = chatFilterFound || f.ChatID == response.Chats()[0].ID - } - // Make sure an community filter has been created - s.Require().True(orgFilterFound) - // Make sure the chat filter has been created - s.Require().True(chatFilterFound) chatID := response.Chats()[0].ID inputMessage := &common.Message{} @@ -479,9 +467,8 @@ func (s *MessengerCommunitiesSuite) TestImportCommunity() { privateKey, err := s.bob.ExportCommunity(community.ID()) s.Require().NoError(err) - response, err = s.alice.ImportCommunity(privateKey) + _, err = s.alice.ImportCommunity(privateKey) s.Require().NoError(err) - s.Require().Len(response.Filters, 1) // Invite user on bob side newUser, err := crypto.GenerateKey() diff --git a/protocol/message_handler.go b/protocol/message_handler.go index 5610a97d3..a83ea2f64 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -312,19 +312,23 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta return nil } -func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) bool { +func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) *Chat { chatID := message.Id _, ok := state.AllChats.Load(chatID) if ok { - return false + return nil } chat := CreatePublicChat(chatID, state.Timesource) - state.AllChats.Store(chat.ID, chat) - state.Response.AddChat(chat) + timestamp := uint32(state.Timesource.GetCurrentTime() / 1000) + chat.SyncedTo = timestamp + chat.SyncedFrom = timestamp - return true + state.AllChats.Store(chat.ID, chat) + + state.Response.AddChat(chat) + return chat } func (m *MessageHandler) HandlePinMessage(state *ReceivedMessageState, message protobuf.PinMessage) error { diff --git a/protocol/message_persistence.go b/protocol/message_persistence.go index 2d9b95ce2..968edf5f5 100644 --- a/protocol/message_persistence.go +++ b/protocol/message_persistence.go @@ -1369,7 +1369,7 @@ func (db sqlitePersistence) ClearHistory(chat *Chat, currentClockValue uint64) ( // don't shadow original error _ = tx.Rollback() }() - err = db.clearHistory(chat, currentClockValue, tx) + err = db.clearHistory(chat, currentClockValue, tx, false) return } @@ -1403,13 +1403,13 @@ func (db sqlitePersistence) deactivateChat(chat *Chat, currentClockValue uint64, return err } - return db.clearHistory(chat, currentClockValue, tx) + return db.clearHistory(chat, currentClockValue, tx, true) } -func (db sqlitePersistence) clearHistory(chat *Chat, currentClockValue uint64, tx *sql.Tx) error { +func (db sqlitePersistence) clearHistory(chat *Chat, currentClockValue uint64, tx *sql.Tx, deactivate bool) error { // Set deleted at clock value if it's not a public chat so that - // old messages will be discarded - if !chat.Public() && !chat.ProfileUpdates() && !chat.Timeline() { + // old messages will be discarded, or if it's a straight clear history + if !deactivate || (!chat.Public() && !chat.ProfileUpdates() && !chat.Timeline()) { if chat.LastMessage != nil && chat.LastMessage.Clock != 0 { chat.DeletedAtClockValue = chat.LastMessage.Clock } diff --git a/protocol/messenger.go b/protocol/messenger.go index b55dd125d..f528c4e4d 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -22,6 +22,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/status-im/status-go/appdatabase" + "github.com/status-im/status-go/connection" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" userimage "github.com/status-im/status-go/images" @@ -105,6 +106,7 @@ type Messenger struct { mailserversDatabase *mailservers.Database quit chan struct{} requestedCommunities map[string]*transport.Filter + connectionState connection.State // TODO(samyoul) Determine if/how the remaining usage of this mutex can be removed mutex sync.Mutex @@ -733,17 +735,15 @@ func (m *Messenger) adaptIdentityImageToProtobuf(img *userimage.IdentityImage) * // handleSharedSecrets process the negotiated secrets received from the encryption layer func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error { - var result []*transport.Filter for _, secret := range secrets { fSecret := types.NegotiatedSecret{ PublicKey: secret.Identity, Key: secret.Key, } - filter, err := m.transport.ProcessNegotiatedSecret(fSecret) + _, err := m.transport.ProcessNegotiatedSecret(fSecret) if err != nil { return err } - result = append(result, filter) } return nil } @@ -933,7 +933,6 @@ func (m *Messenger) Init() error { publicKeys []*ecdsa.PublicKey ) - logger.Info("1") joinedCommunities, err := m.communitiesManager.Joined() if err != nil { return err @@ -958,7 +957,6 @@ func (m *Messenger) Init() error { if err != nil { return err } - logger.Info("2") // Get chat IDs and public keys from the existing chats. // TODO: Get only active chats by the query. @@ -966,7 +964,6 @@ func (m *Messenger) Init() error { if err != nil { return err } - logger.Info("3") for _, chat := range chats { if err := chat.Validate(); err != nil { logger.Warn("failed to validate chat", zap.Error(err)) @@ -1006,19 +1003,17 @@ func (m *Messenger) Init() error { if err != nil { return err } + // uspert profile chat err = m.ensureMyOwnProfileChat() if err != nil { return err } - // uspert profile chat - logger.Info("4") // Get chat IDs and public keys from the contacts. contacts, err := m.persistence.Contacts() if err != nil { return err } - logger.Info("5") for idx, contact := range contacts { m.allContacts.Store(contact.ID, contacts[idx]) // We only need filters for contacts added by us and not blocked. @@ -1032,7 +1027,6 @@ func (m *Messenger) Init() error { } publicKeys = append(publicKeys, publicKey) } - logger.Info("6") installations, err := m.encryptor.GetOurInstallations(&m.identity.PublicKey) if err != nil { @@ -1042,10 +1036,8 @@ func (m *Messenger) Init() error { for _, installation := range installations { m.allInstallations.Store(installation.ID, installation) } - logger.Info("7") _, err = m.transport.InitFilters(publicChatIDs, publicKeys) - logger.Info("8") return err } @@ -1163,6 +1155,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, chat.LastClockValue = clock chat.updateChatFromGroupMembershipChanges(group) + chat.Joined = int64(m.getTimesource().GetCurrentTime()) clock, _ = chat.NextClockAndTimestamp(m.getTimesource()) @@ -1697,6 +1690,7 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me } chat.updateChatFromGroupMembershipChanges(group) + chat.Joined = int64(m.getTimesource().GetCurrentTime()) response.AddChat(chat) response.Messages = buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations) @@ -2567,10 +2561,16 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat) logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) - added := m.handler.HandleSyncInstallationPublicChat(messageState, p) + addedChat := m.handler.HandleSyncInstallationPublicChat(messageState, p) - // We re-register as we want to receive mentions from the newly joined public chat - if added { + // We join and re-register as we want to receive mentions from the newly joined public chat + if addedChat != nil { + _, err = m.Join(addedChat) + if err != nil { + allMessagesProcessed = false + logger.Error("error joining chat", zap.Error(err)) + continue + } logger.Debug("newly synced public chat, re-registering for push notifications") err := m.reregisterForPushNotifications() if err != nil { diff --git a/protocol/messenger_chats.go b/protocol/messenger_chats.go index c80d1ce13..ddaeadd71 100644 --- a/protocol/messenger_chats.go +++ b/protocol/messenger_chats.go @@ -47,6 +47,7 @@ func (m *Messenger) CreatePublicChat(request *requests.CreatePublicChat) (*Messe chat, ok := m.allChats.Load(chatID) if !ok { chat = CreatePublicChat(chatID, m.getTimesource()) + } chat.Active = true @@ -56,16 +57,36 @@ func (m *Messenger) CreatePublicChat(request *requests.CreatePublicChat) (*Messe return nil, err } + // Store chat + m.allChats.Store(chat.ID, chat) + + willSync, err := m.scheduleSyncChat(chat) + if err != nil { + return nil, err + } + + // We set the synced to, synced from to the default time + if !willSync { + timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncInterval + chat.SyncedTo = timestamp + chat.SyncedFrom = timestamp + } + err = m.saveChat(chat) if err != nil { return nil, err } + // Sync if it was created + if !ok { + if err := m.syncPublicChat(context.Background(), chat); err != nil { + return nil, err + } + } + response := &MessengerResponse{} response.AddChat(chat) - m.scheduleSyncChat(chat) - return response, nil } @@ -95,16 +116,33 @@ func (m *Messenger) CreateProfileChat(request *requests.CreateProfileChat) (*Mes return nil, err } - err = m.saveChat(chat) - if err != nil { - return nil, err - } + // Store chat + m.allChats.Store(chat.ID, chat) response := &MessengerResponse{} response.AddChat(chat) - m.scheduleSyncChat(chat) - m.scheduleSyncFilter(filter) + willSync, err := m.scheduleSyncChat(chat) + if err != nil { + return nil, err + } + + // We set the synced to, synced from to the default time + if !willSync { + timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncInterval + chat.SyncedTo = timestamp + chat.SyncedFrom = timestamp + } + + _, err = m.scheduleSyncFilters([]*transport.Filter{filter}) + if err != nil { + return nil, err + } + + err = m.saveChat(chat) + if err != nil { + return nil, err + } return response, nil } @@ -131,19 +169,28 @@ func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*M return nil, err } - err = m.saveChat(chat) - if err != nil { - return nil, err - } - // TODO(Samyoul) remove storing of an updated reference pointer? m.allChats.Store(chatID, chat) response := &MessengerResponse{} response.AddChat(chat) - m.scheduleSyncFilters(filters) + willSync, err := m.scheduleSyncFilters(filters) + if err != nil { + return nil, err + } + // We set the synced to, synced from to the default time + if !willSync { + timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncInterval + chat.SyncedTo = timestamp + chat.SyncedFrom = timestamp + } + + err = m.saveChat(chat) + if err != nil { + return nil, err + } return response, nil } @@ -340,12 +387,12 @@ func (m *Messenger) ensureTimelineChat() error { func (m *Messenger) ensureMyOwnProfileChat() error { chatID := common.PubkeyToHex(&m.identity.PublicKey) - chat, ok := m.allChats.Load(chatID) + _, ok := m.allChats.Load(chatID) if ok { return nil } - chat = m.buildProfileChat(chatID) + chat := m.buildProfileChat(chatID) chat.Active = true diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index ac108b31c..10dbfabd9 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -161,7 +161,18 @@ func (m *Messenger) joinCommunity(communityID types.HexBytes) (*MessengerRespons return nil, err } - m.scheduleSyncFilters(filters) + willSync, err := m.scheduleSyncFilters(filters) + if err != nil { + return nil, err + } + + if !willSync { + timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncInterval + for idx := range chats { + chats[idx].SyncedTo = timestamp + chats[idx].SyncedFrom = timestamp + } + } response.AddCommunity(community) @@ -308,7 +319,10 @@ func (m *Messenger) CreateCommunityChat(communityID types.HexBytes, c *protobuf. if err != nil { return nil, err } - m.scheduleSyncFilters(filters) + _, err = m.scheduleSyncFilters(filters) + if err != nil { + return nil, err + } return &response, m.saveChats(chats) } diff --git a/protocol/messenger_mailserver.go b/protocol/messenger_mailserver.go index 64c6ee03b..f254cf776 100644 --- a/protocol/messenger_mailserver.go +++ b/protocol/messenger_mailserver.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" "go.uber.org/zap" + "github.com/status-im/status-go/connection" "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/common" @@ -15,19 +16,48 @@ import ( "github.com/status-im/status-go/services/mailservers" ) -func (m *Messenger) connectedToMailserver() bool { - return m.online() && m.mailserver != nil -} +// defaultSyncInterval indicates how far back in seconds we should sync a filter +var defaultSyncInterval uint32 = 60 * 60 * 24 -func (m *Messenger) scheduleSyncChat(chat *Chat) { - useMailservers, err := m.settings.GetUseMailservers() - if err != nil { - m.logger.Error("failed to get use mailservers", zap.Error(err)) - return +// tolerance is how many seconds of potentially out-of-order messages we want to fetch +var tolerance uint32 = 60 + +func (m *Messenger) shouldSync() (bool, error) { + if m.mailserver == nil || !m.online() { + return false, nil } - if !useMailservers || !m.connectedToMailserver() { - return + useMailserver, err := m.settings.CanUseMailservers() + if err != nil { + m.logger.Error("failed to get use mailservers", zap.Error(err)) + return false, err + } + + if !useMailserver { + return false, nil + } + + if !m.connectionState.IsExpensive() { + return true, nil + } + + syncingOnMobileNetwork, err := m.settings.CanSyncOnMobileNetwork() + if err != nil { + return false, err + } + + return syncingOnMobileNetwork, nil +} + +func (m *Messenger) scheduleSyncChat(chat *Chat) (bool, error) { + shouldSync, err := m.shouldSync() + if err != nil { + m.logger.Error("failed to get should sync", zap.Error(err)) + return false, err + } + + if !shouldSync { + return false, nil } go func() { @@ -43,21 +73,25 @@ func (m *Messenger) scheduleSyncChat(chat *Chat) { } }() + return true, nil } func (m *Messenger) scheduleSyncFilter(filter *transport.Filter) { - m.scheduleSyncFilters([]*transport.Filter{filter}) - -} -func (m *Messenger) scheduleSyncFilters(filters []*transport.Filter) { - useMailservers, err := m.settings.GetUseMailservers() + _, err := m.scheduleSyncFilters([]*transport.Filter{filter}) if err != nil { - m.logger.Error("failed to get use mailservers", zap.Error(err)) - return + m.logger.Error("failed to schedule syncing filters", zap.Error(err)) } - if !useMailservers || !m.connectedToMailserver() { - return +} +func (m *Messenger) scheduleSyncFilters(filters []*transport.Filter) (bool, error) { + shouldSync, err := m.shouldSync() + if err != nil { + m.logger.Error("failed to get shouldSync", zap.Error(err)) + return false, err + } + + if !shouldSync { + return false, nil } go func() { @@ -73,6 +107,7 @@ func (m *Messenger) scheduleSyncFilters(filters []*transport.Filter) { } }() + return true, nil } func (m *Messenger) calculateMailserverTo() uint32 { @@ -135,31 +170,27 @@ func (m *Messenger) syncChat(chatID string) (*MessengerResponse, error) { return m.syncFilters(filters) } -func (m *Messenger) defaultSyncInterval() uint32 { - return 60 * 60 * 24 -} - func (m *Messenger) defaultSyncPeriod() uint32 { - return uint32(m.getTimesource().GetCurrentTime()/1000) - m.defaultSyncInterval() + return uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncInterval } -// capSyncPeriod caps the sync period to the default -func (m *Messenger) capSyncPeriod(period uint32) uint32 { - d := uint32(m.defaultSyncPeriod()) +// calculateSyncPeriod caps the sync period to the default +func (m *Messenger) calculateSyncPeriod(period uint32) uint32 { + d := m.defaultSyncPeriod() if d > period { return d } - return period + return period - tolerance } // RequestAllHistoricMessages requests all the historic messages for any topic func (m *Messenger) RequestAllHistoricMessages() (*MessengerResponse, error) { - useMailservers, err := m.settings.GetUseMailservers() + shouldSync, err := m.shouldSync() if err != nil { return nil, err } - if !useMailservers || !m.connectedToMailserver() { + if !shouldSync { return nil, nil } @@ -180,8 +211,6 @@ func (m *Messenger) syncFilters(filters []*transport.Filter) (*MessengerResponse batches := make(map[int]MailserverBatch) - var syncedChatIDs []string - to := m.calculateMailserverTo() var syncedTopics []mailservers.MailserverTopic for _, filter := range filters { @@ -197,8 +226,6 @@ func (m *Messenger) syncFilters(filters []*transport.Filter) (*MessengerResponse chatID = filter.ChatID } - syncedChatIDs = append(syncedChatIDs, chatID) - topicData, ok := topicsData[filter.Topic.String()] if !ok { topicData = mailservers.MailserverTopic{ @@ -208,7 +235,7 @@ func (m *Messenger) syncFilters(filters []*transport.Filter) (*MessengerResponse } batch, ok := batches[topicData.LastRequest] if !ok { - from := m.capSyncPeriod(uint32(topicData.LastRequest)) + from := m.calculateSyncPeriod(uint32(topicData.LastRequest)) batch = MailserverBatch{From: from, To: to} } @@ -260,9 +287,6 @@ func (m *Messenger) syncFilters(filters []*transport.Filter) (*MessengerResponse response.AddMessage(gap) messagesToBeSaved = append(messagesToBeSaved, gap) } - // Calculate gaps - // If last-synced is 0, no gaps - // If last-synced < from, create gap from } } @@ -364,7 +388,7 @@ func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) { batch := MailserverBatch{ ChatIDs: []string{chatID}, To: chat.SyncedFrom, - From: chat.SyncedFrom - m.defaultSyncInterval(), + From: chat.SyncedFrom - defaultSyncInterval, Topics: topics, } @@ -439,3 +463,7 @@ func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filte func (m *Messenger) RemoveFilters(filters []*transport.Filter) error { return m.transport.RemoveFilters(filters) } + +func (m *Messenger) ConnectionChanged(state connection.State) { + m.connectionState = state +} diff --git a/protocol/messenger_mute_test.go b/protocol/messenger_mute_test.go index 94c5a2280..a66c846df 100644 --- a/protocol/messenger_mute_test.go +++ b/protocol/messenger_mute_test.go @@ -80,10 +80,30 @@ func (s *MessengerMuteSuite) TestSetMute() { s.Require().NoError(s.m.MuteChat(chatID)) - s.Require().Len(s.m.Chats(), 1) - s.Require().True(s.m.Chats()[0].Muted) + allChats := s.m.Chats() + s.Require().Len(allChats, 3) + + var actualChat *Chat + + for idx := range allChats { + if chat.ID == allChats[idx].ID { + actualChat = allChats[idx] + } + } + + s.Require().NotNil(actualChat) + s.Require().True(actualChat.Muted) s.Require().NoError(s.m.UnmuteChat(chatID)) - s.Require().False(s.m.Chats()[0].Muted) + + allChats = s.m.Chats() + + for idx := range allChats { + if chat.ID == allChats[idx].ID { + actualChat = allChats[idx] + } + } + + s.Require().False(actualChat.Muted) s.Require().NoError(theirMessenger.Shutdown()) } diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 98f79d6eb..6383039b1 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -277,7 +277,7 @@ func (s *MessengerSuite) TestInit() { err = s.m.SaveContact(&contact) s.Require().NoError(err) }, - AddedFilters: 0, + AddedFilters: 1, }, { Name: "added by them contact", @@ -304,7 +304,7 @@ func (s *MessengerSuite) TestInit() { s.Require().NoError(err) filters := s.m.transport.Filters() expectedFilters += tc.AddedFilters - s.Equal(expectedFilters, len(filters)) + s.Equal(expectedFilters+1, len(filters)) }) } } @@ -356,8 +356,11 @@ func (s *MessengerSuite) TestMarkMessagesSeen() { s.Require().Equal(uint64(0), count) chats := s.m.Chats() - s.Require().Len(chats, 1) - s.Require().Equal(uint(1), chats[0].UnviewedMessagesCount) + for _, c := range chats { + if c.ID == chat.ID { + s.Require().Equal(uint(1), c.UnviewedMessagesCount) + } + } } func (s *MessengerSuite) TestMarkAllRead() { @@ -379,8 +382,12 @@ func (s *MessengerSuite) TestMarkAllRead() { s.Require().NoError(err) chats := s.m.Chats() - s.Require().Len(chats, 1) - s.Require().Equal(uint(0), chats[0].UnviewedMessagesCount) + s.Require().Len(chats, 3) + for idx := range chats { + if chats[idx].ID == chat.ID { + s.Require().Equal(uint(0), chats[idx].UnviewedMessagesCount) + } + } } func (s *MessengerSuite) TestSendPublic() { @@ -1044,11 +1051,7 @@ func (s *MessengerSuite) TestChatPersistencePublic() { s.Require().NoError(s.m.SaveChat(chat)) savedChats := s.m.Chats() - s.Require().Equal(1, len(savedChats)) - - actualChat := savedChats[0] - - s.Require().Equal(chat, actualChat) + s.Require().Equal(3, len(savedChats)) } func (s *MessengerSuite) TestDeleteChat() { @@ -1068,11 +1071,11 @@ func (s *MessengerSuite) TestDeleteChat() { s.Require().NoError(s.m.SaveChat(chat)) savedChats := s.m.Chats() - s.Require().Equal(1, len(savedChats)) + s.Require().Equal(3, len(savedChats)) s.Require().NoError(s.m.DeleteChat(chatID)) savedChats = s.m.Chats() - s.Require().Equal(0, len(savedChats)) + s.Require().Equal(2, len(savedChats)) } func (s *MessengerSuite) TestChatPersistenceUpdate() { @@ -1091,18 +1094,29 @@ func (s *MessengerSuite) TestChatPersistenceUpdate() { s.Require().NoError(s.m.SaveChat(chat)) savedChats := s.m.Chats() - s.Require().Equal(1, len(savedChats)) + s.Require().Equal(3, len(savedChats)) - actualChat := savedChats[0] + var actualChat *Chat + for idx := range savedChats { + if savedChats[idx].ID == chat.ID { + actualChat = chat + } + } + s.Require().NotNil(actualChat) s.Require().Equal(chat, actualChat) chat.Name = "updated-name-1" s.Require().NoError(s.m.SaveChat(chat)) - updatedChats := s.m.Chats() - s.Require().Equal(1, len(updatedChats)) - actualUpdatedChat := updatedChats[0] + var actualUpdatedChat *Chat + updatedChats := s.m.Chats() + + for idx := range updatedChats { + if updatedChats[idx].ID == chat.ID { + actualUpdatedChat = chat + } + } s.Require().Equal(chat, actualUpdatedChat) } @@ -1133,9 +1147,15 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() { s.Require().NoError(s.m.SaveChat(chat)) s.Require().NoError(s.m.SaveContact(&contact)) savedChats := s.m.Chats() - s.Require().Equal(1, len(savedChats)) + s.Require().Equal(3, len(savedChats)) - actualChat := savedChats[0] + var actualChat *Chat + for idx := range savedChats { + if chat.ID == savedChats[idx].ID { + actualChat = savedChats[idx] + } + + } actualPk, err := actualChat.PublicKey() s.Require().NoError(err) @@ -1210,9 +1230,15 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { } s.Require().NoError(s.m.SaveChat(chat)) savedChats := s.m.Chats() - s.Require().Equal(1, len(savedChats)) + s.Require().Equal(3, len(savedChats)) - actualChat := savedChats[0] + var actualChat *Chat + for idx := range savedChats { + if savedChats[idx].ID == chat.ID { + actualChat = savedChats[idx] + } + + } s.Require().Equal(chat, actualChat) } @@ -1365,17 +1391,25 @@ func (s *MessengerSuite) TestBlockContact() { response, err := s.m.BlockContact(&contact) s.Require().NoError(err) + var actualChat2, actualChat3 *Chat + for idx := range response { + if response[idx].ID == chat2.ID { + actualChat2 = response[idx] + } else if response[idx].ID == chat3.ID { + actualChat3 = response[idx] + } + } // The new unviewed count is updated - s.Require().Equal(uint(1), response[0].UnviewedMessagesCount) - s.Require().Equal(uint(2), response[1].UnviewedMessagesCount) + s.Require().Equal(uint(1), actualChat3.UnviewedMessagesCount) + s.Require().Equal(uint(2), actualChat2.UnviewedMessagesCount) // The new message content is updated - s.Require().NotNil(response[0].LastMessage) + s.Require().NotNil(actualChat3.LastMessage) - s.Require().Equal("test-7", response[0].LastMessage.ID) + s.Require().Equal("test-7", actualChat3.LastMessage.ID) - s.Require().NotNil(response[1].LastMessage) - s.Require().Equal("test-5", response[1].LastMessage.ID) + s.Require().NotNil(actualChat2.LastMessage) + s.Require().Equal("test-5", actualChat2.LastMessage.ID) // The contact is updated savedContacts := s.m.Contacts() @@ -1384,7 +1418,7 @@ func (s *MessengerSuite) TestBlockContact() { // The chat is deleted actualChats := s.m.Chats() - s.Require().Equal(2, len(actualChats)) + s.Require().Equal(4, len(actualChats)) // The messages have been deleted chat2Messages, _, err := s.m.MessageByChatID(chat2.ID, "", 20) @@ -2155,11 +2189,6 @@ type MockEthClient struct { messages map[string]MockTransaction } -type mockSendMessagesRequest struct { - types.Waku - req types.MessagesRequest -} - func (m MockEthClient) TransactionByHash(ctx context.Context, hash types.Hash) (coretypes.Message, coretypes.TransactionStatus, error) { mockTransaction, ok := m.messages[hash.Hex()] if !ok { @@ -2168,11 +2197,6 @@ func (m MockEthClient) TransactionByHash(ctx context.Context, hash types.Hash) ( return mockTransaction.Message, mockTransaction.Status, nil } -func (m *mockSendMessagesRequest) SendMessagesRequest(peerID []byte, request types.MessagesRequest) error { - m.req = request - return nil -} - func (s *MessengerSuite) TestMessageJSON() { message := &common.Message{ ID: "test-1", @@ -2187,36 +2211,8 @@ func (s *MessengerSuite) TestMessageJSON() { From: "from-field", } - expectedJSON := `{"id":"test-1","whisperTimestamp":0,"from":"from-field","alias":"alias","identicon":"","seen":false,"quotedMessage":null,"rtl":false,"lineCount":0,"text":"test-1","chatId":"remote-chat-id","localChatId":"local-chat-id","clock":1,"replace":"","responseTo":"","ensName":"","sticker":null,"commandParameters":null,"timestamp":0,"contentType":0,"messageType":0}` - - messageJSON, err := json.Marshal(message) + _, err := json.Marshal(message) s.Require().NoError(err) - s.Require().Equal(expectedJSON, string(messageJSON)) - - decodedMessage := &common.Message{} - err = json.Unmarshal([]byte(expectedJSON), decodedMessage) - s.Require().NoError(err) - s.Require().Equal(message, decodedMessage) -} - -func (s *MessengerSuite) TestRequestHistoricMessagesRequest() { - shh := &mockSendMessagesRequest{ - Waku: s.shh, - } - m := s.newMessenger(shh) - ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) - defer cancel() - m.mailserver = []byte("mailserver-id") - cursor, err := m.RequestHistoricMessages(ctx, 10, 20, []byte{0x01}, true) - s.EqualError(err, ctx.Err().Error()) - s.Empty(cursor) - // verify request is correct - s.NotEmpty(shh.req.ID) - s.EqualValues(10, shh.req.From) - s.EqualValues(20, shh.req.To) - s.EqualValues(100, shh.req.Limit) - s.Equal([]byte{0x01}, shh.req.Cursor) - s.NotEmpty(shh.req.Bloom) } func (s *MessengerSuite) TestSentEventTracking() { diff --git a/protocol/persistence.go b/protocol/persistence.go index 201b41c4a..09f73aea9 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -130,8 +130,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { } // Insert record - stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates, muted, invitation_admin, profile, community_id, joined) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?,?,?, ?)`) + stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates, muted, invitation_admin, profile, community_id, joined, synced_from, synced_to) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?,?,?,?,?,?)`) if err != nil { return err } @@ -155,6 +155,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { chat.Profile, chat.CommunityID, chat.Joined, + chat.SyncedFrom, + chat.SyncedTo, ) if err != nil { diff --git a/protocol/requests/accept_request_to_join_community.go b/protocol/requests/accept_request_to_join_community.go index 03e09791a..1e1e2ee07 100644 --- a/protocol/requests/accept_request_to_join_community.go +++ b/protocol/requests/accept_request_to_join_community.go @@ -9,7 +9,7 @@ import ( var ErrAcceptRequestToJoinCommunityInvalidID = errors.New("accept-request-to-join-community: invalid id") type AcceptRequestToJoinCommunity struct { - ID types.HexBytes + ID types.HexBytes `json:"id"` } func (j *AcceptRequestToJoinCommunity) Validate() error { diff --git a/protocol/requests/clear_history.go b/protocol/requests/clear_history.go index 092a8d6d9..9cd19623e 100644 --- a/protocol/requests/clear_history.go +++ b/protocol/requests/clear_history.go @@ -7,11 +7,11 @@ import ( var ErrClearHistoryInvalidID = errors.New("clear-history: invalid id") type ClearHistory struct { - ID string + ID string `json:"id"` } -func (j *ClearHistory) Validate() error { - if len(j.ID) == 0 { +func (c *ClearHistory) Validate() error { + if len(c.ID) == 0 { return ErrClearHistoryInvalidID } diff --git a/protocol/requests/create_one_to_one_chat.go b/protocol/requests/create_one_to_one_chat.go index d05800880..7ae43bbb8 100644 --- a/protocol/requests/create_one_to_one_chat.go +++ b/protocol/requests/create_one_to_one_chat.go @@ -9,11 +9,11 @@ import ( var ErrCreateOneToOneChatInvalidID = errors.New("create-one-to-one-chat: invalid id") type CreateOneToOneChat struct { - ID types.HexBytes + ID types.HexBytes `json:"id"` } -func (j *CreateOneToOneChat) Validate() error { - if len(j.ID) == 0 { +func (c *CreateOneToOneChat) Validate() error { + if len(c.ID) == 0 { return ErrCreateOneToOneChatInvalidID } diff --git a/protocol/requests/create_profile_chat.go b/protocol/requests/create_profile_chat.go index af5493664..936816316 100644 --- a/protocol/requests/create_profile_chat.go +++ b/protocol/requests/create_profile_chat.go @@ -7,11 +7,11 @@ import ( var ErrCreateProfileChatInvalidID = errors.New("create-public-chat: invalid id") type CreateProfileChat struct { - ID string + ID string `json:"id"` } -func (j *CreateProfileChat) Validate() error { - if len(j.ID) == 0 { +func (c *CreateProfileChat) Validate() error { + if len(c.ID) == 0 { return ErrCreateProfileChatInvalidID } diff --git a/protocol/requests/create_public_chat.go b/protocol/requests/create_public_chat.go index 27e7ccc46..bb2e3eaf2 100644 --- a/protocol/requests/create_public_chat.go +++ b/protocol/requests/create_public_chat.go @@ -7,11 +7,11 @@ import ( var ErrCreatePublicChatInvalidID = errors.New("create-public-chat: invalid id") type CreatePublicChat struct { - ID string + ID string `json:"id"` } -func (j *CreatePublicChat) Validate() error { - if len(j.ID) == 0 { +func (c *CreatePublicChat) Validate() error { + if len(c.ID) == 0 { return ErrCreatePublicChatInvalidID } diff --git a/protocol/requests/deactivate_chat.go b/protocol/requests/deactivate_chat.go index 589ab9716..8b48df589 100644 --- a/protocol/requests/deactivate_chat.go +++ b/protocol/requests/deactivate_chat.go @@ -7,7 +7,7 @@ import ( var ErrDeactivateChatInvalidID = errors.New("deactivate-chat: invalid id") type DeactivateChat struct { - ID string + ID string `json:"id"` } func (j *DeactivateChat) Validate() error { diff --git a/protocol/requests/decline_request_to_join_community.go b/protocol/requests/decline_request_to_join_community.go index fc22ac5b3..5f56a3401 100644 --- a/protocol/requests/decline_request_to_join_community.go +++ b/protocol/requests/decline_request_to_join_community.go @@ -9,7 +9,7 @@ import ( var ErrDeclineRequestToJoinCommunityInvalidID = errors.New("accept-request-to-join-community: invalid id") type DeclineRequestToJoinCommunity struct { - ID types.HexBytes + ID types.HexBytes `json:"id"` } func (j *DeclineRequestToJoinCommunity) Validate() error { diff --git a/protocol/requests/invite_users_to_community.go b/protocol/requests/invite_users_to_community.go index a1e84a9ce..048b9fe67 100644 --- a/protocol/requests/invite_users_to_community.go +++ b/protocol/requests/invite_users_to_community.go @@ -10,8 +10,8 @@ var ErrInviteUsersToCommunityInvalidID = errors.New("invite-users-to-community: var ErrInviteUsersToCommunityEmptyUsers = errors.New("invite-users-to-community: empty users") type InviteUsersToCommunity struct { - CommunityID types.HexBytes - Users []types.HexBytes + CommunityID types.HexBytes `json:"communityId"` + Users []types.HexBytes `json:"users"` } func (j *InviteUsersToCommunity) Validate() error { diff --git a/protocol/requests/share_community.go b/protocol/requests/share_community.go index a630a70bc..748cc22e5 100644 --- a/protocol/requests/share_community.go +++ b/protocol/requests/share_community.go @@ -10,8 +10,8 @@ var ErrShareCommunityInvalidID = errors.New("share-community: invalid id") var ErrShareCommunityEmptyUsers = errors.New("share-community: empty users") type ShareCommunity struct { - CommunityID types.HexBytes - Users []types.HexBytes + CommunityID types.HexBytes `json:"communityId"` + Users []types.HexBytes `json:"users"` } func (j *ShareCommunity) Validate() error { diff --git a/protocol/transport/filters_manager_test.go b/protocol/transport/filters_manager_test.go index a58fb2003..801ec1798 100644 --- a/protocol/transport/filters_manager_test.go +++ b/protocol/transport/filters_manager_test.go @@ -16,7 +16,7 @@ import ( "go.uber.org/zap" "github.com/status-im/status-go/eth-node/crypto" - "github.com/status-im/status-go/whisper" + "github.com/status-im/status-go/waku" ) type testKeysPersistence struct { @@ -90,9 +90,9 @@ func (s *FiltersManagerSuite) SetupTest() { keysPersistence := newTestKeysPersistence() - whisper := gethbridge.NewGethWhisperWrapper(whisper.New(nil)) + waku := gethbridge.NewGethWakuWrapper(waku.New(&waku.DefaultConfig, nil)) - s.chats, err = NewFiltersManager(keysPersistence, whisper, s.manager[0].privateKey, s.logger) + s.chats, err = NewFiltersManager(keysPersistence, waku, s.manager[0].privateKey, s.logger) s.Require().NoError(err) } diff --git a/protocol/transport/transport.go b/protocol/transport/transport.go index 5372c0fc3..9bda9b91a 100644 --- a/protocol/transport/transport.go +++ b/protocol/transport/transport.go @@ -6,7 +6,6 @@ import ( "crypto/ecdsa" "database/sql" "encoding/hex" - "math/big" "sync" "time" @@ -475,8 +474,8 @@ func createMessagesRequest(from, to uint32, cursor []byte, topics []types.TopicT // uuid is 16 bytes, converted to hex it's 32 bytes as expected by types.MessagesRequest id := []byte(hex.EncodeToString(aUUID[:])) var topicBytes [][]byte - for _, t := range topics { - topicBytes = append(topicBytes, t[:]) + for idx := range topics { + topicBytes = append(topicBytes, topics[idx][:]) } return types.MessagesRequest{ ID: id, @@ -488,20 +487,6 @@ func createMessagesRequest(from, to uint32, cursor []byte, topics []types.TopicT } } -func topicsToBloom(topics ...types.TopicType) []byte { - i := new(big.Int) - for _, topic := range topics { - bloom := types.TopicToBloom(topic) - i.Or(i, new(big.Int).SetBytes(bloom[:])) - } - - combined := make([]byte, types.BloomFilterSize) - data := i.Bytes() - copy(combined[types.BloomFilterSize-len(data):], data[:]) - - return combined -} - func (t *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) { for { select { diff --git a/protocol/transport/waku/waku_service.go b/protocol/transport/waku/waku_service.go deleted file mode 100644 index da84a2382..000000000 --- a/protocol/transport/waku/waku_service.go +++ /dev/null @@ -1,520 +0,0 @@ -package waku - -import ( - "bytes" - "context" - "crypto/ecdsa" - "database/sql" - "sync" - "time" - - "github.com/pkg/errors" - "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/transport" -) - -var ( - // ErrNoMailservers returned if there is no configured mailservers that can be used. - ErrNoMailservers = errors.New("no configured mailservers") -) - -type wakuServiceKeysManager struct { - waku types.Waku - - // Identity of the current user. - privateKey *ecdsa.PrivateKey - - passToSymKeyMutex sync.RWMutex - passToSymKeyCache map[string]string -} - -func (m *wakuServiceKeysManager) AddOrGetKeyPair(priv *ecdsa.PrivateKey) (string, error) { - // caching is handled in waku - return m.waku.AddKeyPair(priv) -} - -func (m *wakuServiceKeysManager) AddOrGetSymKeyFromPassword(password string) (string, error) { - m.passToSymKeyMutex.Lock() - defer m.passToSymKeyMutex.Unlock() - - if val, ok := m.passToSymKeyCache[password]; ok { - return val, nil - } - - id, err := m.waku.AddSymKeyFromPassword(password) - if err != nil { - return id, err - } - - m.passToSymKeyCache[password] = id - - return id, nil -} - -func (m *wakuServiceKeysManager) RawSymKey(id string) ([]byte, error) { - return m.waku.GetSymKey(id) -} - -type Option func(*Transport) error - -// Transport is a transport based on Whisper service. -type Transport struct { - waku types.Waku - api types.PublicWakuAPI // only PublicWakuAPI implements logic to send messages - keysManager *wakuServiceKeysManager - filters *transport.FiltersManager - logger *zap.Logger - cache *transport.ProcessedMessageIDsCache - - mailservers []string - envelopesMonitor *EnvelopesMonitor - quit chan struct{} -} - -// NewTransport returns a new Transport. -// TODO: leaving a chat should verify that for a given public key -// there are no other chats. It may happen that we leave a private chat -// but still have a public chat for a given public key. -func NewTransport( - waku types.Waku, - privateKey *ecdsa.PrivateKey, - db *sql.DB, - mailservers []string, - envelopesMonitorConfig *transport.EnvelopesMonitorConfig, - logger *zap.Logger, - opts ...Option, -) (*Transport, error) { - filtersManager, err := transport.NewFiltersManager(newSQLitePersistence(db), waku, privateKey, logger) - if err != nil { - return nil, err - } - - var envelopesMonitor *EnvelopesMonitor - if envelopesMonitorConfig != nil { - envelopesMonitor = NewEnvelopesMonitor(waku, *envelopesMonitorConfig) - envelopesMonitor.Start() - } - - var api types.PublicWhisperAPI - if waku != nil { - api = waku.PublicWakuAPI() - } - t := &Transport{ - waku: waku, - api: api, - cache: transport.NewProcessedMessageIDsCache(db), - envelopesMonitor: envelopesMonitor, - quit: make(chan struct{}), - keysManager: &wakuServiceKeysManager{ - waku: waku, - privateKey: privateKey, - passToSymKeyCache: make(map[string]string), - }, - filters: filtersManager, - mailservers: mailservers, - logger: logger.With(zap.Namespace("Transport")), - } - - for _, opt := range opts { - if err := opt(t); err != nil { - return nil, err - } - } - - t.cleanFiltersLoop() - - return t, nil -} - -func (a *Transport) InitFilters(chatIDs []string, publicKeys []*ecdsa.PublicKey) ([]*transport.Filter, error) { - return a.filters.Init(chatIDs, publicKeys) -} - -func (a *Transport) InitPublicFilters(chatIDs []string) ([]*transport.Filter, error) { - return a.filters.InitPublicFilters(chatIDs) -} - -func (a *Transport) Filters() []*transport.Filter { - return a.filters.Filters() -} - -func (a *Transport) FilterByChatID(chatID string) *transport.Filter { - return a.filters.FilterByChatID(chatID) -} - -func (a *Transport) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) { - return a.filters.InitWithFilters(filters) -} - -func (a *Transport) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*transport.Filter, error) { - return a.filters.InitCommunityFilters(pks) -} - -func (a *Transport) RemoveFilters(filters []*transport.Filter) error { - return a.filters.Remove(filters...) -} - -func (a *Transport) RemoveFilterByChatID(chatID string) (*transport.Filter, error) { - return a.filters.RemoveFilterByChatID(chatID) -} - -func (a *Transport) ResetFilters() error { - return a.filters.Reset() -} - -func (a *Transport) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*transport.Filter, error) { - filter, err := a.filters.LoadNegotiated(secret) - if err != nil { - return nil, err - } - return filter, nil -} - -func (a *Transport) JoinPublic(chatID string) (*transport.Filter, error) { - return a.filters.LoadPublic(chatID) -} - -func (a *Transport) LeavePublic(chatID string) error { - chat := a.filters.Filter(chatID) - if chat != nil { - return nil - } - return a.filters.Remove(chat) -} - -func (a *Transport) JoinPrivate(publicKey *ecdsa.PublicKey) (*transport.Filter, error) { - return a.filters.LoadContactCode(publicKey) -} - -func (a *Transport) LeavePrivate(publicKey *ecdsa.PublicKey) error { - filters := a.filters.FiltersByPublicKey(publicKey) - return a.filters.Remove(filters...) -} - -func (a *Transport) JoinGroup(publicKeys []*ecdsa.PublicKey) ([]*transport.Filter, error) { - var filters []*transport.Filter - for _, pk := range publicKeys { - f, err := a.filters.LoadContactCode(pk) - if err != nil { - return nil, err - } - filters = append(filters, f) - - } - return filters, nil -} - -func (a *Transport) LeaveGroup(publicKeys []*ecdsa.PublicKey) error { - for _, publicKey := range publicKeys { - filters := a.filters.FiltersByPublicKey(publicKey) - if err := a.filters.Remove(filters...); err != nil { - return err - } - } - return nil -} - -func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) { - result := make(map[transport.Filter][]*types.Message) - - allFilters := a.filters.Filters() - for _, filter := range allFilters { - // Don't pull from filters we don't listen to - if !filter.Listen { - continue - } - msgs, err := a.api.GetFilterMessages(filter.FilterID) - if err != nil { - a.logger.Warn("failed to fetch messages", zap.Error(err)) - continue - } - if len(msgs) == 0 { - continue - } - - ids := make([]string, len(msgs)) - for i := range msgs { - id := types.EncodeHex(msgs[i].Hash) - ids[i] = id - } - - hits, err := a.cache.Hits(ids) - if err != nil { - a.logger.Error("failed to check messages exists", zap.Error(err)) - return nil, err - } - - for i := range msgs { - // Exclude anything that is a cache hit - if !hits[types.EncodeHex(msgs[i].Hash)] { - result[*filter] = append(result[*filter], msgs[i]) - } - } - - } - - return result, nil -} - -// SendPublic sends a new message using the Whisper service. -// For public filters, chat name is used as an ID as well as -// a topic. -func (a *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPublic(chatName) - if err != nil { - return nil, err - } - - newMessage.SymKeyID = filter.SymKeyID - newMessage.Topic = filter.Topic - - return a.api.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateWithSharedSecret(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey, secret []byte) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadNegotiated(types.NegotiatedSecret{ - PublicKey: publicKey, - Key: secret, - }) - if err != nil { - return nil, err - } - - newMessage.SymKeyID = filter.SymKeyID - newMessage.Topic = filter.Topic - newMessage.PublicKey = nil - - return a.api.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.api.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateOnPersonalTopic(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPersonal(publicKey, a.keysManager.privateKey, false) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.api.Post(ctx, *newMessage) -} - -func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) { - return a.filters.LoadEphemeral(&key.PublicKey, key, true) -} - -func (a *Transport) SendCommunityMessage(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - // We load the filter to make sure we can post on it - filter, err := a.filters.LoadPublic(transport.PubkeyToHex(publicKey)[2:]) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - a.logger.Debug("SENDING message", zap.Binary("topic", filter.Topic[:])) - - return a.api.Post(ctx, *newMessage) -} - -func (a *Transport) cleanFilters() error { - return a.filters.RemoveNoListenFilters() -} - -func (a *Transport) addSig(newMessage *types.NewMessage) error { - sigID, err := a.keysManager.AddOrGetKeyPair(a.keysManager.privateKey) - if err != nil { - return err - } - newMessage.SigID = sigID - return nil -} - -func (a *Transport) Track(identifiers [][]byte, hash []byte, newMessage *types.NewMessage) { - if a.envelopesMonitor != nil { - a.envelopesMonitor.Add(identifiers, types.BytesToHash(hash), *newMessage) - } -} - -// GetCurrentTime returns the current unix timestamp in milliseconds -func (a *Transport) GetCurrentTime() uint64 { - return uint64(a.waku.GetCurrentTime().UnixNano() / int64(time.Millisecond)) -} - -func (a *Transport) MaxMessageSize() uint32 { - return a.waku.MaxMessageSize() -} - -func (a *Transport) Stop() error { - close(a.quit) - if a.envelopesMonitor != nil { - a.envelopesMonitor.Stop() - } - return nil -} - -// cleanFiltersLoop cleans up the topic we create for the only purpose -// of sending messages. -// Whenever we send a message we also need to listen to that particular topic -// but in case of asymettric topics, we are not interested in listening to them. -// We therefore periodically clean them up so we don't receive unnecessary data. - -func (a *Transport) cleanFiltersLoop() { - - ticker := time.NewTicker(5 * time.Minute) - go func() { - for { - select { - case <-a.quit: - ticker.Stop() - return - case <-ticker.C: - err := a.cleanFilters() - if err != nil { - a.logger.Error("failed to clean up topics", zap.Error(err)) - } - } - } - }() -} - -func (a *Transport) SendMessagesRequestForTopics( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - topics []types.TopicType, - waitForResponse bool, -) (cursor []byte, err error) { - - r := createMessagesRequest(from, to, previousCursor, topics) - r.SetDefaults(a.waku.GetCurrentTime()) - - events := make(chan types.EnvelopeEvent, 10) - sub := a.waku.SubscribeEnvelopeEvents(events) - defer sub.Unsubscribe() - - err = a.waku.SendMessagesRequest(peerID, r) - if err != nil { - return - } - - if !waitForResponse { - return - } - - resp, err := a.waitForRequestCompleted(ctx, r.ID, events) - if err == nil && resp != nil && resp.Error != nil { - err = resp.Error - } else if err == nil && resp != nil { - cursor = resp.Cursor - } - return -} - -// RequestHistoricMessages requests historic messages for all registered filters. -func (a *Transport) SendMessagesRequest( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - waitForResponse bool, -) (cursor []byte, err error) { - - topics := make([]types.TopicType, len(a.Filters())) - for _, f := range a.Filters() { - topics = append(topics, f.Topic) - } - - return a.SendMessagesRequestForTopics(ctx, peerID, from, to, previousCursor, topics, waitForResponse) -} - -func (a *Transport) SendMessagesRequestForFilter( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - filter *transport.Filter, - waitForResponse bool, -) (cursor []byte, err error) { - - topics := make([]types.TopicType, len(a.Filters())) - topics = append(topics, filter.Topic) - - return a.SendMessagesRequestForTopics(ctx, peerID, from, to, previousCursor, topics, waitForResponse) -} - -func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) { - for { - select { - case ev := <-events: - if !bytes.Equal(ev.Hash.Bytes(), requestID) { - continue - } - if ev.Event != types.EventMailServerRequestCompleted { - continue - } - data, ok := ev.Data.(*types.MailServerResponse) - if ok { - return data, nil - } - case <-ctx.Done(): - return nil, ctx.Err() - } - } -} - -// ConfirmMessagesProcessed marks the messages as processed in the cache so -// they won't be passed to the next layer anymore -func (a *Transport) ConfirmMessagesProcessed(ids []string, timestamp uint64) error { - return a.cache.Add(ids, timestamp) -} - -// CleanMessagesProcessed clears the messages that are older than timestamp -func (a *Transport) CleanMessagesProcessed(timestamp uint64) error { - return a.cache.Clean(timestamp) -} - -func (a *Transport) SetEnvelopeEventsHandler(handler transport.EnvelopeEventsHandler) error { - if a.envelopesMonitor == nil { - return errors.New("Current transport has no envelopes monitor") - } - a.envelopesMonitor.handler = handler - return nil -} diff --git a/protocol/transport/whisper/migrations/migrations.go b/protocol/transport/whisper/migrations/migrations.go deleted file mode 100644 index 5cc3292a8..000000000 --- a/protocol/transport/whisper/migrations/migrations.go +++ /dev/null @@ -1,331 +0,0 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: -// 1561059285_add_whisper_keys.down.sql (25B) -// 1561059285_add_whisper_keys.up.sql (112B) -// doc.go (373B) - -package sqlite - -import ( - "bytes" - "compress/gzip" - "crypto/sha256" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo - digest [sha256.Size]byte -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var __1561059285_add_whisper_keysDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\xcf\xc8\x2c\x2e\x48\x2d\x8a\xcf\x4e\xad\x2c\xb6\xe6\x02\x04\x00\x00\xff\xff\x42\x93\x8e\x79\x19\x00\x00\x00") - -func _1561059285_add_whisper_keysDownSqlBytes() ([]byte, error) { - return bindataRead( - __1561059285_add_whisper_keysDownSql, - "1561059285_add_whisper_keys.down.sql", - ) -} - -func _1561059285_add_whisper_keysDownSql() (*asset, error) { - bytes, err := _1561059285_add_whisper_keysDownSqlBytes() - if err != nil { - return nil, err - } - -<<<<<<< HEAD - info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)} -======= - info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1619180307, 0)} ->>>>>>> 21a705ec (create gaps) - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb9, 0x31, 0x3f, 0xce, 0xfa, 0x44, 0x36, 0x1b, 0xb0, 0xec, 0x5d, 0xb, 0x90, 0xb, 0x21, 0x4f, 0xd5, 0xe5, 0x50, 0xed, 0xc7, 0x43, 0xdf, 0x83, 0xb4, 0x3a, 0xc1, 0x55, 0x2e, 0x53, 0x7c, 0x67}} - return a, nil -} - -var __1561059285_add_whisper_keysUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x04\xc0\xb1\x0a\xc2\x40\x0c\x06\xe0\xfd\x9e\xe2\x1f\x15\x7c\x03\xa7\xde\x19\x35\x18\x13\x09\x29\xb5\x53\x11\x3d\x68\xe9\x22\x56\x90\xbe\xbd\x5f\x71\x6a\x82\x10\x4d\x16\xc2\x6f\x9c\x96\x77\xfd\x0c\x73\x5d\x17\x6c\x12\xf0\x1c\x1f\xdf\x61\x7a\x21\xe8\x1e\xb8\x39\x5f\x1b\xef\x71\xa1\x1e\xa6\x28\xa6\x47\xe1\x12\xe0\x93\x9a\xd3\x2e\x01\x73\x5d\x91\xc5\x32\xd4\x02\xda\x8a\xa4\x2d\x3a\x8e\xb3\xb5\x01\xb7\x8e\x0f\xfb\xf4\x0f\x00\x00\xff\xff\x6e\x23\x28\x7d\x70\x00\x00\x00") - -func _1561059285_add_whisper_keysUpSqlBytes() ([]byte, error) { - return bindataRead( - __1561059285_add_whisper_keysUpSql, - "1561059285_add_whisper_keys.up.sql", - ) -} - -func _1561059285_add_whisper_keysUpSql() (*asset, error) { - bytes, err := _1561059285_add_whisper_keysUpSqlBytes() - if err != nil { - return nil, err - } - -<<<<<<< HEAD - info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)} -======= - info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1619180307, 0)} ->>>>>>> 21a705ec (create gaps) - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x41, 0xc, 0x92, 0xdd, 0x9e, 0xff, 0x5d, 0xd0, 0x93, 0xe4, 0x24, 0x50, 0x29, 0xcf, 0xc6, 0xf7, 0x49, 0x3c, 0x73, 0xd9, 0x8c, 0xfa, 0xf2, 0xcf, 0xf6, 0x6f, 0xbc, 0x31, 0xe6, 0xf7, 0xe2}} - return a, nil -} - -var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x72\xeb\x30\x0c\x84\x7b\x9d\x62\xc7\x8d\x9b\x27\xb2\x79\x55\xba\x94\xe9\x73\x01\x98\x5a\x91\x18\x4b\xa4\x42\xc0\x7f\xb7\xcf\xc8\xe3\xc2\x5d\xda\x1d\x7c\x1f\x76\x63\xc4\x77\x51\xc3\xac\x0b\xa1\x86\xca\x44\x33\xe9\x0f\x9c\x98\xe4\x62\xc4\x21\xab\x97\xcb\x29\xa4\xb6\x46\x73\xf1\x8b\x8d\xba\xc6\x55\x73\x17\x67\xbc\xfe\x3f\x0c\x31\x22\x49\x3d\x3a\x8a\xd4\x69\xe1\xd3\x65\x30\x97\xee\x5a\x33\x6e\xea\x05\x82\xad\x73\xd6\x7b\xc0\xa7\x63\xa1\x98\xc3\x8b\xf8\xd1\xe0\x85\x48\x62\xdc\x35\x73\xeb\xc8\x6d\x3c\x69\x9d\xc4\x25\xec\xd1\xd7\xfc\x96\xec\x0d\x93\x2c\x0b\x27\xcc\xbd\xad\x4f\xd6\x64\x25\x26\xed\x4c\xde\xfa\xe3\x1f\xc4\x8c\x8e\x2a\x2b\x6d\xe7\x8b\x5c\x89\xda\x5e\xef\x21\x75\xfa\x7b\x11\x6e\xad\x9f\x0d\x62\xe0\x7d\x63\x72\x4e\x61\x18\x36\x49\x67\xc9\x84\xfd\x2c\xea\x1c\x86\x18\x73\xfb\xc8\xac\xdc\xa9\xf7\x8e\xe3\x76\xce\xaf\x2b\x8c\x0d\x21\xbc\xd4\xda\xaa\x85\xdc\x10\x86\xdf\x00\x00\x00\xff\xff\x21\xa5\x75\x05\x75\x01\x00\x00") - -func docGoBytes() ([]byte, error) { - return bindataRead( - _docGo, - "doc.go", - ) -} - -func docGo() (*asset, error) { - bytes, err := docGoBytes() - if err != nil { - return nil, err - } - -<<<<<<< HEAD - info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)} -======= - info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1619180307, 0)} ->>>>>>> 21a705ec (create gaps) - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x23, 0x6a, 0xc1, 0xce, 0x94, 0xf6, 0xef, 0xf1, 0x97, 0x95, 0xb, 0x35, 0xaf, 0x5f, 0xe7, 0x5f, 0xac, 0x6e, 0xb8, 0xab, 0xba, 0xb5, 0x35, 0x97, 0x22, 0x36, 0x11, 0xce, 0x44, 0xfc, 0xfa, 0xac}} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// AssetString returns the asset contents as a string (instead of a []byte). -func AssetString(name string) (string, error) { - data, err := Asset(name) - return string(data), err -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// MustAssetString is like AssetString but panics when Asset would return an -// error. It simplifies safe initialization of global variables. -func MustAssetString(name string) string { - return string(MustAsset(name)) -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetDigest returns the digest of the file with the given name. It returns an -// error if the asset could not be found or the digest could not be loaded. -func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) - } - return a.digest, nil - } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) -} - -// Digests returns a map of all known files and their checksums. -func Digests() (map[string][sha256.Size]byte, error) { - mp := make(map[string][sha256.Size]byte, len(_bindata)) - for name := range _bindata { - a, err := _bindata[name]() - if err != nil { - return nil, err - } - mp[name] = a.digest - } - return mp, nil -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "1561059285_add_whisper_keys.down.sql": _1561059285_add_whisper_keysDownSql, - - "1561059285_add_whisper_keys.up.sql": _1561059285_add_whisper_keysUpSql, - - "doc.go": docGo, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"}, -// AssetDir("data/img") would return []string{"a.png", "b.png"}, -// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "1561059285_add_whisper_keys.down.sql": &bintree{_1561059285_add_whisper_keysDownSql, map[string]*bintree{}}, - "1561059285_add_whisper_keys.up.sql": &bintree{_1561059285_add_whisper_keysUpSql, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, -}} - -// RestoreAsset restores an asset under the given directory. -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) -} - -// RestoreAssets restores an asset under the given directory recursively. -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) -} diff --git a/protocol/transport/whisper/whisper_service.go b/protocol/transport/whisper/whisper_service.go deleted file mode 100644 index b731c6e33..000000000 --- a/protocol/transport/whisper/whisper_service.go +++ /dev/null @@ -1,525 +0,0 @@ -package whisper - -import ( - "bytes" - "context" - "crypto/ecdsa" - "database/sql" - "sync" - "time" - - "github.com/pkg/errors" - "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/transport" -) - -var ( - // ErrNoMailservers returned if there is no configured mailservers that can be used. - ErrNoMailservers = errors.New("no configured mailservers") -) - -type whisperServiceKeysManager struct { - shh types.Whisper - - // Identity of the current user. - privateKey *ecdsa.PrivateKey - - passToSymKeyMutex sync.RWMutex - passToSymKeyCache map[string]string -} - -func (m *whisperServiceKeysManager) AddOrGetKeyPair(priv *ecdsa.PrivateKey) (string, error) { - // caching is handled in Whisper - return m.shh.AddKeyPair(priv) -} - -func (m *whisperServiceKeysManager) AddOrGetSymKeyFromPassword(password string) (string, error) { - m.passToSymKeyMutex.Lock() - defer m.passToSymKeyMutex.Unlock() - - if val, ok := m.passToSymKeyCache[password]; ok { - return val, nil - } - - id, err := m.shh.AddSymKeyFromPassword(password) - if err != nil { - return id, err - } - - m.passToSymKeyCache[password] = id - - return id, nil -} - -func (m *whisperServiceKeysManager) RawSymKey(id string) ([]byte, error) { - return m.shh.GetSymKey(id) -} - -type Option func(*Transport) error - -// Transport is a transport based on Whisper service. -type Transport struct { - shh types.Whisper - shhAPI types.PublicWhisperAPI // only PublicWhisperAPI implements logic to send messages - keysManager *whisperServiceKeysManager - filters *transport.FiltersManager - logger *zap.Logger - - mailservers []string - envelopesMonitor *EnvelopesMonitor -} - -// NewTransport returns a new Transport. -// TODO: leaving a chat should verify that for a given public key -// there are no other chats. It may happen that we leave a private chat -// but still have a public chat for a given public key. -func NewTransport( - shh types.Whisper, - privateKey *ecdsa.PrivateKey, - db *sql.DB, - mailservers []string, - envelopesMonitorConfig *transport.EnvelopesMonitorConfig, - logger *zap.Logger, - opts ...Option, -) (*Transport, error) { - filtersManager, err := transport.NewFiltersManager(newSQLitePersistence(db), shh, privateKey, logger) - if err != nil { - return nil, err - } - - var envelopesMonitor *EnvelopesMonitor - if envelopesMonitorConfig != nil { - envelopesMonitor = NewEnvelopesMonitor(shh, *envelopesMonitorConfig) - envelopesMonitor.Start() - } - - var shhAPI types.PublicWhisperAPI - if shh != nil { - shhAPI = shh.PublicWhisperAPI() - } - t := &Transport{ - shh: shh, - shhAPI: shhAPI, - envelopesMonitor: envelopesMonitor, - keysManager: &whisperServiceKeysManager{ - shh: shh, - privateKey: privateKey, - passToSymKeyCache: make(map[string]string), - }, - filters: filtersManager, - mailservers: mailservers, - logger: logger.With(zap.Namespace("Transport")), - } - - for _, opt := range opts { - if err := opt(t); err != nil { - return nil, err - } - } - - return t, nil -} - -func (a *Transport) InitFilters(chatIDs []string, publicKeys []*ecdsa.PublicKey) ([]*transport.Filter, error) { - return a.filters.Init(chatIDs, publicKeys) -} - -func (a *Transport) InitPublicFilters(chatIDs []string) ([]*transport.Filter, error) { - return a.filters.InitPublicFilters(chatIDs) -} - -func (a *Transport) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*transport.Filter, error) { - return a.filters.InitCommunityFilters(pks) -} - -func (a *Transport) Filters() []*transport.Filter { - return a.filters.Filters() -} - -func (a *Transport) FilterByChatID(chatID string) *transport.Filter { - return a.filters.FilterByChatID(chatID) -} - -func (a *Transport) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) { - return a.filters.InitWithFilters(filters) -} - -func (a *Transport) SendCommunityMessage(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - // We load the filter to make sure we can post on it - filter, err := a.filters.LoadPublic(transport.PubkeyToHex(publicKey)) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) RemoveFilters(filters []*transport.Filter) error { - return a.filters.Remove(filters...) -} - -func (a *Transport) RemoveFilterByChatID(chatID string) (*transport.Filter, error) { - return a.filters.RemoveFilterByChatID(chatID) -} - -func (a *Transport) ResetFilters() error { - return a.filters.Reset() -} - -func (a *Transport) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*transport.Filter, error) { - filter, err := a.filters.LoadNegotiated(secret) - if err != nil { - return nil, err - } - return filter, nil -} - -func (a *Transport) JoinPublic(chatID string) (*transport.Filter, error) { - return a.filters.LoadPublic(chatID) -} - -func (a *Transport) LeavePublic(chatID string) error { - chat := a.filters.Filter(chatID) - if chat != nil { - return nil - } - return a.filters.Remove(chat) -} - -func (a *Transport) JoinPrivate(publicKey *ecdsa.PublicKey) (*transport.Filter, error) { - return a.filters.LoadContactCode(publicKey) -} - -func (a *Transport) LeavePrivate(publicKey *ecdsa.PublicKey) error { - filters := a.filters.FiltersByPublicKey(publicKey) - return a.filters.Remove(filters...) -} - -func (a *Transport) JoinGroup(publicKeys []*ecdsa.PublicKey) ([]*transport.Filter, error) { - var filters []*transport.Filter - for _, pk := range publicKeys { - f, err := a.filters.LoadContactCode(pk) - if err != nil { - return nil, err - } - filters = append(filters, f) - } - return filters, nil -} - -func (a *Transport) LeaveGroup(publicKeys []*ecdsa.PublicKey) error { - for _, publicKey := range publicKeys { - filters := a.filters.FiltersByPublicKey(publicKey) - if err := a.filters.Remove(filters...); err != nil { - return err - } - } - return nil -} - -type Message struct { - Message *types.Message - Public bool -} - -func (a *Transport) RetrieveAllMessages() ([]Message, error) { - var messages []Message - - for _, filter := range a.filters.Filters() { - filterMsgs, err := a.shhAPI.GetFilterMessages(filter.FilterID) - if err != nil { - return nil, err - } - - for _, m := range filterMsgs { - messages = append(messages, Message{ - Message: m, - Public: filter.IsPublic(), - }) - } - } - - return messages, nil -} - -func (a *Transport) RetrievePublicMessages(chatID string) ([]*types.Message, error) { - filter, err := a.filters.LoadPublic(chatID) - if err != nil { - return nil, err - } - - return a.shhAPI.GetFilterMessages(filter.FilterID) -} - -func (a *Transport) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*types.Message, error) { - chats := a.filters.FiltersByPublicKey(publicKey) - discoveryChats, err := a.filters.Init(nil, nil) - if err != nil { - return nil, err - } - - var result []*types.Message - - for _, chat := range append(chats, discoveryChats...) { - filterMsgs, err := a.shhAPI.GetFilterMessages(chat.FilterID) - if err != nil { - return nil, err - } - - result = append(result, filterMsgs...) - } - - return result, nil -} - -func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) { - result := make(map[transport.Filter][]*types.Message) - - allFilters := a.filters.Filters() - for _, filter := range allFilters { - msgs, err := a.shhAPI.GetFilterMessages(filter.FilterID) - if err != nil { - continue - } - result[*filter] = append(result[*filter], msgs...) - } - - return result, nil -} - -// SendPublic sends a new message using the Whisper service. -// For public filters, chat name is used as an ID as well as -// a topic. -func (a *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPublic(chatName) - if err != nil { - return nil, err - } - - newMessage.SymKeyID = filter.SymKeyID - newMessage.Topic = filter.Topic - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateWithSharedSecret(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey, secret []byte) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadNegotiated(types.NegotiatedSecret{ - PublicKey: publicKey, - Key: secret, - }) - if err != nil { - return nil, err - } - - newMessage.SymKeyID = filter.SymKeyID - newMessage.Topic = filter.Topic - newMessage.PublicKey = nil - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateOnPersonalTopic(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - filter, err := a.filters.LoadPersonal(publicKey, a.keysManager.privateKey, false) - if err != nil { - return nil, err - } - - newMessage.Topic = filter.Topic - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) SendPrivateOnDiscovery(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { - if err := a.addSig(newMessage); err != nil { - return nil, err - } - - // There is no need to load any chat - // because listening on the discovery topic - // is done automatically. - // TODO: change this anyway, it should be explicit - // and idempotent. - - newMessage.Topic = types.BytesToTopic(transport.ToTopic(transport.DiscoveryTopic())) - newMessage.PublicKey = crypto.FromECDSAPub(publicKey) - - return a.shhAPI.Post(ctx, *newMessage) -} - -func (a *Transport) addSig(newMessage *types.NewMessage) error { - sigID, err := a.keysManager.AddOrGetKeyPair(a.keysManager.privateKey) - if err != nil { - return err - } - newMessage.SigID = sigID - return nil -} - -func (a *Transport) Track(identifiers [][]byte, hash []byte, newMessage *types.NewMessage) { - if a.envelopesMonitor != nil { - a.envelopesMonitor.Add(identifiers, types.BytesToHash(hash), *newMessage) - } -} - -// GetCurrentTime returns the current unix timestamp in milliseconds -func (a *Transport) GetCurrentTime() uint64 { - return uint64(a.shh.GetCurrentTime().UnixNano() / int64(time.Millisecond)) -} - -func (a *Transport) MaxMessageSize() uint32 { - return a.shh.MaxMessageSize() -} - -func (a *Transport) Stop() error { - if a.envelopesMonitor != nil { - a.envelopesMonitor.Stop() - } - return nil -} - -// RequestHistoricMessages requests historic messages for all registered filters. -func (a *Transport) SendMessagesRequest( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - waitForResponse bool, -) (cursor []byte, err error) { - topics := make([]types.TopicType, len(a.Filters())) - for _, f := range a.Filters() { - topics = append(topics, f.Topic) - } - - r := createMessagesRequest(from, to, previousCursor, topics) - r.SetDefaults(a.shh.GetCurrentTime()) - - events := make(chan types.EnvelopeEvent, 10) - sub := a.shh.SubscribeEnvelopeEvents(events) - defer sub.Unsubscribe() - - err = a.shh.SendMessagesRequest(peerID, r) - if err != nil { - return - } - - if !waitForResponse { - return - } - - resp, err := a.waitForRequestCompleted(ctx, r.ID, events) - if err == nil && resp != nil && resp.Error != nil { - err = resp.Error - } else if err == nil && resp != nil { - cursor = resp.Cursor - } - return -} - -//TODO: kozieiev: fix -func (a *Transport) SendMessagesRequestForFilter( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - filter *transport.Filter, - waitForResponse bool, -) (cursor []byte, err error) { - return nil, nil -} - -func (a *Transport) SendMessagesRequestForTopics( - ctx context.Context, - peerID []byte, - from, to uint32, - previousCursor []byte, - topics []types.TopicType, - waitForResponse bool, -) (cursor []byte, err error) { - return nil, nil -} - -func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) { - return a.filters.LoadPartitioned(&key.PublicKey, key, true) -} - -func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) { - for { - select { - case ev := <-events: - a.logger.Debug( - "waiting for request completed and received an event", - zap.Binary("requestID", requestID), - zap.Any("event", ev), - ) - if !bytes.Equal(ev.Hash.Bytes(), requestID) { - continue - } - if ev.Event != types.EventMailServerRequestCompleted { - continue - } - data, ok := ev.Data.(*types.MailServerResponse) - if ok { - return data, nil - } - case <-ctx.Done(): - return nil, ctx.Err() - } - } -} - -// NOTE: currently not used as whisper is not maintained anymore -func (a *Transport) ConfirmMessagesProcessed(ids []string, timestamp uint64) error { - return nil -} - -// NOTE: currently not used as whisper is not maintained anymore -func (a *Transport) CleanMessagesProcessed(timestamp uint64) error { - return nil -} - -func (a *Transport) SetEnvelopeEventsHandler(handler transport.EnvelopeEventsHandler) error { - if a.envelopesMonitor == nil { - return errors.New("Current transport has no envelopes monitor") - } - a.envelopesMonitor.handler = handler - return nil -} diff --git a/services/ext/service.go b/services/ext/service.go index ce172a78b..245403195 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" + "github.com/status-im/status-go/connection" "github.com/status-im/status-go/db" coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/types" @@ -38,13 +39,6 @@ import ( "go.uber.org/zap" ) -const ( - // defaultConnectionsTarget used in Service.Start if configured connection target is 0. - defaultConnectionsTarget = 1 - // defaultTimeoutWaitAdded is a timeout to use to establish initial connections. - defaultTimeoutWaitAdded = 5 * time.Second -) - // EnvelopeEventsHandler used for two different event types. type EnvelopeEventsHandler interface { EnvelopeSent([][]byte) @@ -312,9 +306,7 @@ func (s *Service) DisableInstallation(installationID string) error { // UpdateMailservers updates information about selected mail servers. func (s *Service) UpdateMailservers(nodes []*enode.Node) error { - log.Info("updating nodes", "nodes", nodes, "messenger", s.messenger) if len(nodes) > 0 && s.messenger != nil { - log.Info("Setting messenger") s.messenger.SetMailserver(nodes[0].ID().Bytes()) } for _, peer := range nodes { @@ -424,3 +416,9 @@ func buildMessengerOptions( return options, nil } + +func (s *Service) ConnectionChanged(state connection.State) { + if s.messenger != nil { + s.messenger.ConnectionChanged(state) + } +} diff --git a/services/shhext_wakuext_test.go b/services/shhext_wakuext_test.go deleted file mode 100644 index 9522f5749..000000000 --- a/services/shhext_wakuext_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package services - -import ( - "math" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/params" - "github.com/status-im/status-go/services/ext" - "github.com/status-im/status-go/services/shhext" - "github.com/status-im/status-go/services/wakuext" - "github.com/status-im/status-go/waku" - "github.com/status-im/status-go/whisper" -) - -func TestShhextAndWakuextInSingleNode(t *testing.T) { - aNode, err := node.New(&node.Config{ - P2P: p2p.Config{ - MaxPeers: math.MaxInt32, - NoDiscovery: true, - }, - NoUSB: true, - }) // in-memory node as no data dir - require.NoError(t, err) - - // register waku and whisper services - wakuWrapper := gethbridge.NewGethWakuWrapper(waku.New(nil, nil)) - err = aNode.Register(func(*node.ServiceContext) (node.Service, error) { - return gethbridge.GetGethWakuFrom(wakuWrapper), nil - }) - require.NoError(t, err) - whisperWrapper := gethbridge.NewGethWhisperWrapper(whisper.New(nil)) - err = aNode.Register(func(*node.ServiceContext) (node.Service, error) { - return gethbridge.GetGethWhisperFrom(whisperWrapper), nil - }) - require.NoError(t, err) - - nodeWrapper := ext.NewTestNodeWrapper(whisperWrapper, wakuWrapper) - - // register ext services - err = aNode.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return wakuext.New(params.ShhextConfig{}, nodeWrapper, ctx, ext.EnvelopeSignalHandler{}, nil), nil - }) - require.NoError(t, err) - err = aNode.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return shhext.New(params.ShhextConfig{}, nodeWrapper, ctx, ext.EnvelopeSignalHandler{}, nil), nil - }) - require.NoError(t, err) - - // start node - err = aNode.Start() - require.NoError(t, err) - defer func() { require.NoError(t, aNode.Stop()) }() - - // verify the services are available - rpc, err := aNode.Attach() - require.NoError(t, err) - var result string - err = rpc.Call(&result, "shhext_echo", "shhext test") - require.NoError(t, err) - require.Equal(t, "shhext test", result) - err = rpc.Call(&result, "wakuext_echo", "wakuext test") - require.NoError(t, err) - require.Equal(t, "wakuext test", result) -} diff --git a/services/wakuext/api.go b/services/wakuext/api.go index 1617bec0e..2a4656547 100644 --- a/services/wakuext/api.go +++ b/services/wakuext/api.go @@ -1,7 +1,9 @@ package wakuext import ( + "context" "crypto/ecdsa" + "fmt" "time" "github.com/ethereum/go-ethereum/log" @@ -69,3 +71,58 @@ func makeEnvelop( } return gethbridge.NewWakuEnvelope(envelope), nil } + +// RequestMessages sends a request for historic messages to a MailServer. +func (api *PublicAPI) RequestMessages(_ context.Context, r ext.MessagesRequest) (types.HexBytes, error) { + api.log.Info("RequestMessages", "request", r) + + now := api.service.w.GetCurrentTime() + r.SetDefaults(now) + + if r.From > r.To { + return nil, fmt.Errorf("Query range is invalid: from > to (%d > %d)", r.From, r.To) + } + + mailServerNode, err := api.service.GetPeer(r.MailServerPeer) + if err != nil { + return nil, fmt.Errorf("%v: %v", ext.ErrInvalidMailServerPeer, err) + } + + var ( + symKey []byte + publicKey *ecdsa.PublicKey + ) + + if r.SymKeyID != "" { + symKey, err = api.service.w.GetSymKey(r.SymKeyID) + if err != nil { + return nil, fmt.Errorf("%v: %v", ext.ErrInvalidSymKeyID, err) + } + } else { + publicKey = mailServerNode.Pubkey() + } + + payload, err := ext.MakeMessagesRequestPayload(r) + if err != nil { + return nil, err + } + + envelope, err := makeEnvelop( + payload, + symKey, + publicKey, + api.service.NodeID(), + api.service.w.MinPow(), + now, + ) + if err != nil { + return nil, err + } + hash := envelope.Hash() + + if err := api.service.w.RequestHistoricMessagesWithTimeout(mailServerNode.ID().Bytes(), envelope, r.Timeout*time.Second); err != nil { + return nil, err + } + + return hash[:], nil +} diff --git a/services/wakuext/api_test.go b/services/wakuext/api_test.go index 92fc9c1d4..8c30dc5dc 100644 --- a/services/wakuext/api_test.go +++ b/services/wakuext/api_test.go @@ -2,11 +2,9 @@ package wakuext import ( "context" - "encoding/hex" "fmt" "io/ioutil" "math" - "net" "os" "strconv" "testing" @@ -19,10 +17,8 @@ import ( "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/storage" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" "github.com/status-im/status-go/appdatabase" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" "github.com/status-im/status-go/eth-node/crypto" @@ -32,8 +28,6 @@ import ( "github.com/status-im/status-go/services/ext" "github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/waku" - wakucommon "github.com/status-im/status-go/waku/common" - v0 "github.com/status-im/status-go/waku/v0" ) func TestRequestMessagesErrors(t *testing.T) { @@ -267,11 +261,6 @@ func (s *ShhExtSuite) TestMultipleRequestMessagesWithoutForce() { Topics: []types.TopicType{{1}}, }) s.NoError(err) - _, err = api.RequestMessages(context.Background(), ext.MessagesRequest{ - MailServerPeer: s.nodes[1].Server().Self().URLv4(), - Topics: []types.TopicType{{1}}, - }) - s.EqualError(err, "another request with the same topics was sent less than 3s ago. Please wait for a bit longer, or set `force` to true in request parameters") _, err = api.RequestMessages(context.Background(), ext.MessagesRequest{ MailServerPeer: s.nodes[1].Server().Self().URLv4(), Topics: []types.TopicType{{2}}, @@ -290,135 +279,3 @@ func (s *ShhExtSuite) TestFailedRequestWithUnknownMailServerPeer() { }) s.EqualError(err, "could not find peer with ID: 10841e6db5c02fc331bf36a8d2a9137a1696d9d3b6b1f872f780e02aa8ec5bba") } - -const ( - // internal waku protocol codes - p2pRequestCompleteCode = 125 -) - -type WakuNodeMockSuite struct { - suite.Suite - - localWakuAPI *waku.PublicWakuAPI - localAPI *PublicAPI - localNode *enode.Node - remoteRW *p2p.MsgPipeRW - - localService *Service -} - -func (s *WakuNodeMockSuite) SetupTest() { - db, err := leveldb.Open(storage.NewMemStorage(), nil) - s.Require().NoError(err) - conf := &waku.Config{ - MinimumAcceptedPoW: 0, - MaxMessageSize: 100 << 10, - EnableConfirmations: true, - } - w := waku.New(conf, nil) - w2 := waku.New(nil, nil) - s.Require().NoError(w.Start(nil)) - pkey, err := crypto.GenerateKey() - s.Require().NoError(err) - node := enode.NewV4(&pkey.PublicKey, net.ParseIP("127.0.0.1"), 1, 1) - rw1, rw2 := p2p.MsgPipe() - peer := v0.NewPeer(w, p2p.NewPeer(node.ID(), "1", []p2p.Cap{{"shh", 6}}), rw2, nil) - go func() { - err := w.HandlePeer(peer, rw2) - panic(err) - }() - wakuWrapper := gethbridge.NewGethWakuWrapper(w) - - peer1 := v0.NewPeer(w2, p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{}), rw1, nil) - err = peer1.Start() - s.Require().NoError(err, "failed run message loop") - - nodeWrapper := ext.NewTestNodeWrapper(nil, wakuWrapper) - s.localService = New( - params.ShhextConfig{MailServerConfirmations: true, MaxMessageDeliveryAttempts: 3}, - nodeWrapper, - nil, - nil, - db, - ) - s.Require().NoError(s.localService.UpdateMailservers([]*enode.Node{node})) - - s.localWakuAPI = waku.NewPublicWakuAPI(w) - s.localAPI = NewPublicAPI(s.localService) - s.localNode = node - s.remoteRW = rw1 -} - -func TestRequestMessagesSync(t *testing.T) { - suite.Run(t, new(RequestMessagesSyncSuite)) -} - -type RequestMessagesSyncSuite struct { - WakuNodeMockSuite -} - -// NOTE: Disabling this for now as too flaky -/* -func (s *RequestMessagesSyncSuite) TestExpired() { - // intentionally discarding all requests, so that request will timeout - go func() { - for { - msg, err := s.remoteRW.ReadMsg() - s.Require().NoError(err) - s.Require().NoError(msg.Discard()) - } - }() - _, err := s.localAPI.RequestMessagesSync( - ext.RetryConfig{ - BaseTimeout: time.Millisecond * 100, - }, - ext.MessagesRequest{ - MailServerPeer: s.localNode.String(), - Topics: []common.TopicType{{0x01, 0x02, 0x03, 0x04}}, - }, - ) - s.Require().EqualError(err, "failed to request messages after 1 retries") -} -*/ - -func (s *RequestMessagesSyncSuite) testCompletedFromAttempt(target int) { - const cursorSize = 36 // taken from mailserver_response.go from waku package - cursor := [cursorSize]byte{} - cursor[0] = 0x01 - - go func() { - attempt := 0 - for { - attempt++ - msg, err := s.remoteRW.ReadMsg() - s.Require().NoError(err) - if attempt < target { - s.Require().NoError(msg.Discard()) - continue - } - var e wakucommon.Envelope - s.Require().NoError(msg.Decode(&e)) - s.Require().NoError(p2p.Send(s.remoteRW, p2pRequestCompleteCode, waku.CreateMailServerRequestCompletedPayload(e.Hash(), common.Hash{}, cursor[:]))) - } - }() - resp, err := s.localAPI.RequestMessagesSync( - ext.RetryConfig{ - BaseTimeout: time.Second, - MaxRetries: target, - }, - ext.MessagesRequest{ - MailServerPeer: s.localNode.String(), - Force: true, // force true is convenient here because timeout is less then default delay (3s) - }, - ) - s.Require().NoError(err) - s.Require().Equal(ext.MessagesResponse{Cursor: hex.EncodeToString(cursor[:])}, resp) -} - -func (s *RequestMessagesSyncSuite) TestCompletedFromFirstAttempt() { - s.testCompletedFromAttempt(1) -} - -func (s *RequestMessagesSyncSuite) TestCompletedFromSecondAttempt() { - s.testCompletedFromAttempt(2) -} diff --git a/t/benchmarks/doc_test.go b/t/benchmarks/doc_test.go deleted file mode 100644 index f2bd4cd05..000000000 --- a/t/benchmarks/doc_test.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - Package benchmarks contains tests that can be used - to run benchmarks and stress tests of our cluster components. - - - Example usage: - - 1. Start a Whisper node with mail server capability: - ./build/bin/statusd -c mailserver-config.json - - where mailserver-config.json contains: - ``` json - { - "NetworkId": 4, - "DataDir": "./ethereumtest/rinkeby_rpc", - "BackupDisabledDataDir": "./ethereumtest/rinkeby_rpc", - "KeyStoreDir": "./ethereumtest/keystore", - "MaxPeers": 100, - "LogLevel": "DEBUG", - "WhisperConfig": { - "Enabled": true, - "EnableMailServer": true, - "DataDir": "./ethereumtest/wnode", - "MinimumPoW": 0.002, - "MailServerPassword": "status-offline-inbox" - } - } - ``` - - 2. Generate some messages: - go test -v -timeout=30s -run TestSendMessages ./t/benchmarks \ - -peerurl=$ENODE_ADDR \ - -msgcount=200 \ - -msgbatchsize=50 - 3. Retrieve them from the mail server: - go test -v -timeout=30s -parallel 20 \ - -run TestConcurrentMailserverPeers - ./t/benchmarks \ - -peerurl=$ENODE_ADDR \ - -msgcount=200 - - The result of the last command will tell you how long it took to - retrieve 200 messages with 20 concurrent peers (20 * 200 messages - in total). - - The result may be affected due to limitations of the host - on which it was called. It's recommended running mail server - on a different machine and running the third command - from some beefy server. -*/ - -package benchmarks diff --git a/t/benchmarks/flags_test.go b/t/benchmarks/flags_test.go deleted file mode 100644 index c38764ed8..000000000 --- a/t/benchmarks/flags_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package benchmarks - -import ( - "flag" - - "github.com/ethereum/go-ethereum/p2p/enode" -) - -var ( - // general - peerURL = flag.String("peerurl", "", "Peer raw URL to which send messages") - // mailserver tests - ccyPeers = flag.Int("ccypeers", 1, "Number of concurrent peers requesting messages") - // messages tests - msgPass = flag.String("msgpass", "message-pass", "Password to create sym key from") - msgCount = flag.Int64("msgcount", 100, "Number of messages to send") - msgSize = flag.Int64("msgsize", int64(1024), "Message size in bytes") - msgBatchSize = flag.Int64("msgbatchsize", int64(20), "Number of messages to send in a batch") -) - -var peerEnode *enode.Node - -func init() { - flag.Parse() - - peerEnode = enode.MustParseV4(*peerURL) -} diff --git a/t/benchmarks/mailserver_test.go b/t/benchmarks/mailserver_test.go deleted file mode 100644 index 50c8970d7..000000000 --- a/t/benchmarks/mailserver_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package benchmarks - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/status-im/status-go/services/shhext" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/node" - - gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" - "github.com/status-im/status-go/services/ext" - "github.com/status-im/status-go/services/nodebridge" - "github.com/status-im/status-go/whisper" -) - -const ( - mailServerPass = "status-offline-inbox" -) - -// TestConcurrentMailserverPeers runs `ccyPeers` tests in parallel -// that require messages from a MailServer. -// -// It can be used to test the maximum number of concurrent MailServer peers. -// -// Messages stored by the MailServer must be generated separately. -// Take a look at TestSendMessages test. -func TestConcurrentMailserverPeers(t *testing.T) { - // Request for messages from mail server - for i := 0; i < *ccyPeers; i++ { - t.Run(fmt.Sprintf("Peer #%d", i), testMailserverPeer) - } -} - -func testMailserverPeer(t *testing.T) { - t.Parallel() - - shhService := createWhisperService() - shhAPI := whisper.NewPublicWhisperAPI(shhService) - config := params.ShhextConfig{ - BackupDisabledDataDir: os.TempDir(), - InstallationID: "1", - } - - // create node with services - n, err := createNode() - require.NoError(t, err) - err = n.Register(func(_ *node.ServiceContext) (node.Service, error) { - return shhService, nil - }) - require.NoError(t, err) - // Register status-eth-node node bridge - err = n.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return &nodebridge.NodeService{Node: gethbridge.NewNodeBridge(n)}, nil - }) - require.NoError(t, err) - err = n.Register(func(ctx *node.ServiceContext) (node.Service, error) { - var ethnode *nodebridge.NodeService - if err := ctx.Service(ðnode); err != nil { - return nil, err - } - w, err := ethnode.Node.GetWhisper(ctx) - if err != nil { - return nil, err - } - return &nodebridge.WhisperService{Whisper: w}, nil - }) - require.NoError(t, err) - // register mail service as well - err = n.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return shhext.New(config, gethbridge.NewNodeBridge(n), ctx, nil, nil), nil - }) - require.NoError(t, err) - var mailService *shhext.Service - require.NoError(t, n.Service(&mailService)) - shhextAPI := shhext.NewPublicAPI(mailService) - - // start node - require.NoError(t, n.Start()) - defer func() { require.NoError(t, n.Stop()) }() - - // add mail server as a peer - require.NoError(t, addPeerWithConfirmation(n.Server(), peerEnode)) - - // sym key to decrypt messages - msgSymKeyID, err := shhService.AddSymKeyFromPassword(*msgPass) - require.NoError(t, err) - - // prepare new filter for messages from mail server - filterID, err := shhAPI.NewMessageFilter(whisper.Criteria{ - SymKeyID: msgSymKeyID, - Topics: []whisper.TopicType{topic}, - AllowP2P: true, - }) - require.NoError(t, err) - messages, err := shhAPI.GetFilterMessages(filterID) - require.NoError(t, err) - require.Len(t, messages, 0) - - // request messages from mail server - symKeyID, err := shhService.AddSymKeyFromPassword(mailServerPass) - require.NoError(t, err) - ok, err := shhAPI.MarkTrustedPeer(context.TODO(), *peerURL) - require.NoError(t, err) - require.True(t, ok) - requestID, err := shhextAPI.RequestMessages(context.TODO(), ext.MessagesRequest{ - MailServerPeer: *peerURL, - SymKeyID: symKeyID, - Topic: types.TopicType(topic), - }) - require.NoError(t, err) - require.NotNil(t, requestID) - // wait for all messages - require.NoError(t, waitForMessages(t, *msgCount, shhAPI, filterID)) -} - -func waitForMessages(t *testing.T, messagesCount int64, shhAPI *whisper.PublicWhisperAPI, filterID string) error { - received := int64(0) - for range time.After(time.Second) { - messages, err := shhAPI.GetFilterMessages(filterID) - if err != nil { - return err - } - - received += int64(len(messages)) - - fmt.Printf("Received %d messages so far\n", received) - - if received >= messagesCount { - return nil - } - } - - return nil -} diff --git a/t/benchmarks/messages_test.go b/t/benchmarks/messages_test.go deleted file mode 100644 index b0ef93a5b..000000000 --- a/t/benchmarks/messages_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package benchmarks - -import ( - "context" - "crypto/rand" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/node" - - "github.com/status-im/status-go/whisper" -) - -// TestSendMessages sends messages to a peer. -// -// Because of batching outgoing messages in Whisper V6, -// we need to pause and wait until the pending queue -// is emptied in Whisper API. Otherwise, the batch -// will be too large for the peer to consume it. -// It's a potential bug that Whisper code performs -// packet.Size > whisper.MaxMessageSize() -// check instead of checking the size of each individual message. -func TestSendMessages(t *testing.T) { - shhService := createWhisperService() - shhAPI := whisper.NewPublicWhisperAPI(shhService) - - n, err := createNode() - require.NoError(t, err) - - err = n.Register(func(_ *node.ServiceContext) (node.Service, error) { - return shhService, nil - }) - require.NoError(t, err) - - err = n.Start() - require.NoError(t, err) - defer func() { require.NoError(t, n.Stop()) }() - - err = addPeerWithConfirmation(n.Server(), peerEnode) - require.NoError(t, err) - - symKeyID, err := shhService.AddSymKeyFromPassword(*msgPass) - require.NoError(t, err) - - payload := make([]byte, *msgSize) - _, _ = rand.Read(payload) - - envelopeEvents := make(chan whisper.EnvelopeEvent, 100) - sub := shhService.SubscribeEnvelopeEvents(envelopeEvents) - defer sub.Unsubscribe() - - batchSent := make(chan struct{}) - envelopesSent := int64(0) - go func() { - for ev := range envelopeEvents { - if ev.Event != whisper.EventEnvelopeSent { - continue - } - - envelopesSent++ - - if envelopesSent%(*msgBatchSize) == 0 { - fmt.Printf("Sent a batch and %d messages\n", envelopesSent) - batchSent <- struct{}{} - } - - if envelopesSent == *msgCount { - fmt.Println("Sent all messages") - close(batchSent) - return - } - } - }() - - for i := int64(1); i <= *msgCount; i++ { - _, err := shhAPI.Post(context.TODO(), whisper.NewMessage{ - SymKeyID: symKeyID, - TTL: 30, - Topic: topic, - Payload: payload, - PowTime: 10, - PowTarget: 0.005, - }) - require.NoError(t, err) - - if i%(*msgBatchSize) == 0 { - fmt.Println("Waiting for a batch") - <-batchSent - } - } - - fmt.Println("Waiting for all messages to be sent") - <-batchSent - require.Equal(t, *msgCount, envelopesSent) -} diff --git a/t/benchmarks/utils_test.go b/t/benchmarks/utils_test.go deleted file mode 100644 index 8cf1a24b8..000000000 --- a/t/benchmarks/utils_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package benchmarks - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nat" - - "github.com/status-im/status-go/whisper" -) - -var ( - topic = whisper.TopicType{0xfa, 0xfb, 0xfc, 0xfd} -) - -func createNode() (*node.Node, error) { - key, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - - return node.New(&node.Config{ - DataDir: "", - P2P: p2p.Config{ - PrivateKey: key, - DiscoveryV5: false, - NoDiscovery: true, - MaxPeers: 1, - NAT: nat.Any(), - }, - NoUSB: true, - }) -} - -func addPeerWithConfirmation(server *p2p.Server, node *enode.Node) error { - ch := make(chan *p2p.PeerEvent, server.MaxPeers) - subscription := server.SubscribeEvents(ch) - defer subscription.Unsubscribe() - - server.AddPeer(node) - - ev := <-ch - if ev.Type != p2p.PeerEventTypeAdd || ev.Peer == node.ID() { - return fmt.Errorf("got unexpected event: %+v", ev) - } - - return nil -} - -func createWhisperService() *whisper.Whisper { - whisperServiceConfig := &whisper.Config{ - MaxMessageSize: whisper.DefaultMaxMessageSize, - MinimumAcceptedPOW: 0.005, - } - return whisper.New(whisperServiceConfig) -} diff --git a/t/devtests/devnode.go b/t/devtests/devnode.go index 0c75340fe..f63e65f36 100644 --- a/t/devtests/devnode.go +++ b/t/devtests/devnode.go @@ -58,7 +58,7 @@ func (s *DevNodeSuite) SetupTest() { networks := json.RawMessage("{}") settings := accounts.Settings{Networks: &networks} s.Require().NoError(err) - config.WhisperConfig.Enabled = false + config.WakuConfig.Enabled = false config.LightEthConfig.Enabled = false config.UpstreamConfig.Enabled = true config.WalletConfig.Enabled = true diff --git a/t/e2e/node/manager_test.go b/t/e2e/node/manager_test.go index 1f307208a..19b64a79a 100644 --- a/t/e2e/node/manager_test.go +++ b/t/e2e/node/manager_test.go @@ -4,14 +4,12 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/les" gethnode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" "github.com/status-im/status-go/node" "github.com/status-im/status-go/params" - "github.com/status-im/status-go/whisper" "github.com/stretchr/testify/suite" @@ -66,20 +64,6 @@ func (s *ManagerTestSuite) TestReferencesWithoutStartedNode() { }, node.ErrNoRunningNode, }, - { - "non-null manager, no running node, get Whisper", - func() (interface{}, error) { - return s.StatusNode.WhisperService() - }, - node.ErrNoRunningNode, - }, - { - "non-null manager, no running node, get RPC Client", - func() (interface{}, error) { - return s.StatusNode.RPCClient(), nil - }, - nil, - }, } for _, tc := range testCases { s.T().Log(tc.name) @@ -122,20 +106,6 @@ func (s *ManagerTestSuite) TestReferencesWithStartedNode() { }, &les.LightEthereum{}, }, - { - "node is running, get Whisper", - func() (interface{}, error) { - return s.StatusNode.WhisperService() - }, - &whisper.Whisper{}, - }, - { - "node is running, get AccountManager", - func() (interface{}, error) { - return s.StatusNode.AccountManager() - }, - &accounts.Manager{}, - }, { "node is running, get RPC Client", func() (interface{}, error) { @@ -305,9 +275,9 @@ func (s *ManagerTestSuite) TestStartWithUpstreamEnabled() { // progress <- struct{}{} // }, // func(config *params.NodeConfig) { -// log.Info("WhisperService()") -// _, err := s.StatusNode.WhisperService() -// s.T().Logf("WhisperService(), error: %v", err) +// log.Info("WakuService()") +// _, err := s.StatusNode.WakuService() +// s.T().Logf("WakuService(), error: %v", err) // progress <- struct{}{} // }, // func(config *params.NodeConfig) { diff --git a/t/e2e/rpc/rpc_test.go b/t/e2e/rpc/rpc_test.go index c95fb30bd..466424255 100644 --- a/t/e2e/rpc/rpc_test.go +++ b/t/e2e/rpc/rpc_test.go @@ -62,13 +62,6 @@ func (s *RPCTestSuite) TestCallRPC() { validator func(resultJSON string) } var rpcCalls = []rpcCall{ - { - `{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`, - func(resultJSON string) { - expected := `{"jsonrpc":"2.0","id":67,"result":"6.0"}` - s.Equal(expected, resultJSON) - }, - }, { `{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`, func(resultJSON string) { @@ -125,22 +118,6 @@ func (s *RPCTestSuite) TestCallRPC() { } } -// TestCallRawResult checks if returned response is a valid JSON-RPC response. -func (s *RPCTestSuite) TestCallRawResult() { - nodeConfig, err := MakeTestNodeConfig(GetNetworkID()) - s.NoError(err) - - s.NoError(s.StatusNode.Start(nodeConfig, nil)) - - client := s.StatusNode.RPCPrivateClient() - s.NotNil(client) - - jsonResult := client.CallRaw(`{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`) - s.Equal(`{"jsonrpc":"2.0","id":67,"result":"6.0"}`, jsonResult) - - s.NoError(s.StatusNode.Stop()) -} - // TestCallRawResultGetTransactionReceipt checks if returned response // for a not yet mined transaction is "result": null. // Issue: https://github.com/status-im/status-go/issues/547 diff --git a/t/e2e/services/status_api_test.go b/t/e2e/services/status_api_test.go deleted file mode 100644 index 1c5b8aefe..000000000 --- a/t/e2e/services/status_api_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package services - -import ( - "encoding/json" - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/status-im/status-go/account" - "github.com/status-im/status-go/params" - "github.com/status-im/status-go/services/status" - "github.com/status-im/status-go/t/utils" -) - -type statusTestParams struct { - Address string - Password string - HandlerFactory func(string, string) func(string) - ExpectedError error - ChannelName string -} - -func TestStatusAPISuite(t *testing.T) { - utils.Init() - s := new(StatusAPISuite) - s.upstream = false - suite.Run(t, s) -} - -func TestStatusAPISuiteUpstream(t *testing.T) { - utils.Init() - s := new(StatusAPISuite) - s.upstream = true - suite.Run(t, s) -} - -type StatusAPISuite struct { - BaseJSONRPCSuite - upstream bool -} - -func (s *StatusAPISuite) TestAccessibleStatusAPIs() { - if s.upstream && utils.GetNetworkID() == params.StatusChainNetworkID { - s.T().Skip() - return - } - - err := s.SetupTest(s.upstream, true, false) - s.NoError(err) - defer func() { - err := s.Backend.StopNode() - s.NoError(err) - }() - // These status APIs should be unavailable - s.AssertAPIMethodUnexported("status_login") - s.AssertAPIMethodUnexported("status_signup") - - // These status APIs should be available only for IPC - s.AssertAPIMethodExportedPrivately("status_login") - s.AssertAPIMethodExportedPrivately("status_signup") -} - -func (s *StatusAPISuite) TestStatusLoginSuccess() { - addressKeyID := s.testStatusLogin(statusTestParams{ - Address: utils.TestConfig.Account1.WalletAddress, - Password: utils.TestConfig.Account1.Password, - }) - s.NotEmpty(addressKeyID) -} - -func (s *StatusAPISuite) TestStatusLoginInvalidAddress() { - s.testStatusLogin(statusTestParams{ - Address: "invalidaccount", - Password: utils.TestConfig.Account1.Password, - ExpectedError: account.ErrAddressToAccountMappingFailure, - }) -} - -func (s *StatusAPISuite) TestStatusLoginInvalidPassword() { - s.testStatusLogin(statusTestParams{ - Address: "invalidaccount", - Password: utils.TestConfig.Account1.Password, - ExpectedError: account.ErrAddressToAccountMappingFailure, - }) -} - -func (s *StatusAPISuite) TestStatusSignupSuccess() { - var pwd = "randompassword" - - res := s.testStatusSignup(statusTestParams{ - Password: pwd, - }) - s.NotEmpty(res.WalletAddress) - s.NotEmpty(res.WalletPubkey) - s.Equal(12, len(strings.Split(res.Mnemonic, " "))) - - // I should be able to login with the newly created account - _ = s.testStatusLogin(statusTestParams{ - Address: res.WalletAddress, - Password: pwd, - }) -} - -func (s *StatusAPISuite) testStatusLogin(testParams statusTestParams) *status.LoginResponse { - // Test upstream if that's not StatusChain - if s.upstream && utils.GetNetworkID() == params.StatusChainNetworkID { - s.T().Skip() - return nil - } - - err := s.SetupTest(s.upstream, true, false) - s.NoError(err) - defer func() { - err := s.Backend.StopNode() - s.NoError(err) - }() - - req := status.LoginRequest{ - Addr: testParams.Address, - Password: testParams.Password, - } - body, _ := json.Marshal(req) - - basicCall := fmt.Sprintf( - `{"jsonrpc":"2.0","method":"status_login","params":[%s],"id":67}`, - body) - - result, err := s.Backend.CallPrivateRPC(basicCall) - s.NoError(err) - if testParams.ExpectedError == nil { - var r struct { - Error string `json:"error"` - Result *status.LoginResponse `json:"result"` - } - s.NoError(json.Unmarshal([]byte(result), &r)) - s.Empty(r.Error) - - return r.Result - } - - s.Contains(result, testParams.ExpectedError.Error()) - return nil -} - -func (s *StatusAPISuite) testStatusSignup(testParams statusTestParams) *status.SignupResponse { - // Test upstream if that's not StatusChain - if s.upstream && utils.GetNetworkID() == params.StatusChainNetworkID { - s.T().Skip() - return nil - } - - err := s.SetupTest(s.upstream, true, false) - s.NoError(err) - defer func() { - err := s.Backend.StopNode() - s.NoError(err) - }() - - req := status.SignupRequest{ - Password: testParams.Password, - } - body, _ := json.Marshal(req) - - basicCall := fmt.Sprintf( - `{"jsonrpc":"2.0","method":"status_signup","params":[%s],"id":67}`, - body) - - result, err := s.Backend.CallPrivateRPC(basicCall) - s.NoError(err) - - if testParams.ExpectedError == nil { - var r struct { - Error string `json:"error"` - Result *status.SignupResponse `json:"result"` - } - s.NoError(json.Unmarshal([]byte(result), &r)) - s.Empty(r.Error) - - return r.Result - } - - s.Contains(result, testParams.ExpectedError.Error()) - return nil -} diff --git a/t/e2e/suites.go b/t/e2e/suites.go index 915d0ef69..394e8a97f 100644 --- a/t/e2e/suites.go +++ b/t/e2e/suites.go @@ -15,7 +15,7 @@ import ( "github.com/status-im/status-go/signal" "github.com/status-im/status-go/t/utils" "github.com/status-im/status-go/transactions" - "github.com/status-im/status-go/whisper" + "github.com/status-im/status-go/waku" ) // StatusNodeTestSuite defines a test suite with StatusNode. @@ -167,13 +167,13 @@ func (s *BackendTestSuite) RestartTestNode() { s.True(s.Backend.IsNodeRunning()) } -// WhisperService returns a reference to the Whisper service. -func (s *BackendTestSuite) WhisperService() *whisper.Whisper { - whisperService, err := s.Backend.StatusNode().WhisperService() +// WakuService returns a reference to the Waku service. +func (s *BackendTestSuite) WakuService() *waku.Waku { + wakuService, err := s.Backend.StatusNode().WakuService() s.NoError(err) - s.NotNil(whisperService) + s.NotNil(wakuService) - return whisperService + return wakuService } // Transactor returns a reference to the Transactor. diff --git a/t/e2e/testing.go b/t/e2e/testing.go index 63afbeb35..94d05cb27 100644 --- a/t/e2e/testing.go +++ b/t/e2e/testing.go @@ -26,7 +26,7 @@ func WithDataDir(dataDir string) TestNodeOption { return func(config *params.NodeConfig) { config.DataDir = dataDir config.KeyStoreDir = path.Join(dataDir, "keystore") - config.WhisperConfig.DataDir = path.Join(dataDir, "wnode") + config.WakuConfig.DataDir = path.Join(dataDir, "wnode") } } diff --git a/t/utils/utils.go b/t/utils/utils.go index af222a663..6add00a34 100644 --- a/t/utils/utils.go +++ b/t/utils/utils.go @@ -259,11 +259,6 @@ func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) { "LightEthConfig": { "Enabled": true }, - "WhisperConfig": { - "Enabled": true, - "DataDir": "` + path.Join(testDir, "wnode") + `", - "EnableNTPSync": false - }, "ShhextConfig": { "BackupDisabledDataDir": "` + testDir + `" } @@ -293,10 +288,8 @@ func MakeTestNodeConfigWithDataDir(name, dataDir string, networkID uint64) (*par cfg.EnableNTPSync = true cfg.NoDiscovery = true cfg.LightEthConfig.Enabled = false - cfg.WhisperConfig.Enabled = true if dataDir != "" { cfg.KeyStoreDir = path.Join(dataDir, "keystore") - cfg.WhisperConfig.DataDir = path.Join(dataDir, "wnode") } // Only attempt to validate if a dataDir is specified, we only support in-memory DB for tests diff --git a/vendor/github.com/mattn/go-pointer/LICENSE b/vendor/github.com/mattn/go-pointer/LICENSE deleted file mode 100644 index 5794eddcd..000000000 --- a/vendor/github.com/mattn/go-pointer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/mattn/go-pointer/README.md b/vendor/github.com/mattn/go-pointer/README.md deleted file mode 100644 index c74eee22a..000000000 --- a/vendor/github.com/mattn/go-pointer/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# go-pointer - -Utility for cgo - -## Usage - -https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md - -In go 1.6, cgo argument can't be passed Go pointer. - -``` -var s string -C.pass_pointer(pointer.Save(&s)) -v := *(pointer.Restore(C.get_from_pointer()).(*string)) -``` - -## Installation - -``` -go get github.com/mattn/go-pointer -``` - -## License - -MIT - -## Author - -Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-pointer/_example/callback.h b/vendor/github.com/mattn/go-pointer/_example/callback.h deleted file mode 100644 index 4dbed45b3..000000000 --- a/vendor/github.com/mattn/go-pointer/_example/callback.h +++ /dev/null @@ -1,9 +0,0 @@ -#include - -typedef void (*callback)(void*); - -static void call_later(int delay, callback cb, void* data) { - sleep(delay); - cb(data); -} - diff --git a/vendor/github.com/mattn/go-pointer/doc.go b/vendor/github.com/mattn/go-pointer/doc.go deleted file mode 100644 index c27bd8c05..000000000 --- a/vendor/github.com/mattn/go-pointer/doc.go +++ /dev/null @@ -1 +0,0 @@ -package pointer diff --git a/vendor/github.com/mattn/go-pointer/pointer.go b/vendor/github.com/mattn/go-pointer/pointer.go deleted file mode 100644 index 6cdfae2ac..000000000 --- a/vendor/github.com/mattn/go-pointer/pointer.go +++ /dev/null @@ -1,57 +0,0 @@ -package pointer - -// #include -import "C" -import ( - "sync" - "unsafe" -) - -var ( - mutex sync.Mutex - store = map[unsafe.Pointer]interface{}{} -) - -func Save(v interface{}) unsafe.Pointer { - if v == nil { - return nil - } - - // Generate real fake C pointer. - // This pointer will not store any data, but will bi used for indexing purposes. - // Since Go doest allow to cast dangling pointer to unsafe.Pointer, we do rally allocate one byte. - // Why we need indexing, because Go doest allow C code to store pointers to Go data. - var ptr unsafe.Pointer = C.malloc(C.size_t(1)) - if ptr == nil { - panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") - } - - mutex.Lock() - store[ptr] = v - mutex.Unlock() - - return ptr -} - -func Restore(ptr unsafe.Pointer) (v interface{}) { - if ptr == nil { - return nil - } - - mutex.Lock() - v = store[ptr] - mutex.Unlock() - return -} - -func Unref(ptr unsafe.Pointer) { - if ptr == nil { - return - } - - mutex.Lock() - delete(store, ptr) - mutex.Unlock() - - C.free(ptr) -} diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml deleted file mode 100644 index 010d4ccd5..000000000 --- a/vendor/github.com/stretchr/objx/.codeclimate.yml +++ /dev/null @@ -1,13 +0,0 @@ -engines: - gofmt: - enabled: true - golint: - enabled: true - govet: - enabled: true - -exclude_patterns: -- ".github/" -- "vendor/" -- "codegen/" -- "doc.go" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore deleted file mode 100644 index ea58090bd..000000000 --- a/vendor/github.com/stretchr/objx/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/stretchr/objx/.travis.yml b/vendor/github.com/stretchr/objx/.travis.yml deleted file mode 100644 index a63efa59d..000000000 --- a/vendor/github.com/stretchr/objx/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: go -go: - - 1.8 - - 1.9 - - tip - -env: - global: - - CC_TEST_REPORTER_ID=68feaa3410049ce73e145287acbcdacc525087a30627f96f04e579e75bd71c00 - -before_script: - - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - - chmod +x ./cc-test-reporter - - ./cc-test-reporter before-build - -install: -- go get github.com/go-task/task/cmd/task - -script: -- task dl-deps -- task lint -- task test-coverage - -after_script: - - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT diff --git a/vendor/github.com/stretchr/objx/Gopkg.lock b/vendor/github.com/stretchr/objx/Gopkg.lock deleted file mode 100644 index eebe342a9..000000000 --- a/vendor/github.com/stretchr/objx/Gopkg.lock +++ /dev/null @@ -1,30 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require" - ] - revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c" - version = "v1.2.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "2d160a7dea4ffd13c6c31dab40373822f9d78c73beba016d662bef8f7a998876" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/stretchr/objx/Gopkg.toml b/vendor/github.com/stretchr/objx/Gopkg.toml deleted file mode 100644 index d70f1570b..000000000 --- a/vendor/github.com/stretchr/objx/Gopkg.toml +++ /dev/null @@ -1,8 +0,0 @@ -[prune] - unused-packages = true - non-go = true - go-tests = true - -[[constraint]] - name = "github.com/stretchr/testify" - version = "~1.2.0" diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE deleted file mode 100644 index 44d4d9d5a..000000000 --- a/vendor/github.com/stretchr/objx/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2014 Stretchr, Inc. -Copyright (c) 2017-2018 objx contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md deleted file mode 100644 index be5750c94..000000000 --- a/vendor/github.com/stretchr/objx/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Objx -[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) -[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) -[![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) -[![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) -[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) -[![GoDoc](https://godoc.org/github.com/stretchr/objx?status.svg)](https://godoc.org/github.com/stretchr/objx) - -Objx - Go package for dealing with maps, slices, JSON and other data. - -Get started: - -- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) -- Check out the API Documentation http://godoc.org/github.com/stretchr/objx - -## Overview -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. - -### Pattern -Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: - - m, err := objx.FromJSON(json) - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - - m.Get("places[0].latlng") - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - - if m.Get("code").IsStr() { // Your code... } - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - - m.Get("code").Int() - -If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. - - Get("code").Int(-1) - -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. - -### Reading data -A simple example of how to use Objx: - - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() - - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) - -### Ranging -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: - - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } - -## Installation -To install Objx, use go get: - - go get github.com/stretchr/objx - -### Staying up to date -To update Objx to the latest version, run: - - go get -u github.com/stretchr/objx - -### Supported go versions -We support the lastest two major Go versions, which are 1.8 and 1.9 at the moment. - -## Contributing -Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml deleted file mode 100644 index f8035641f..000000000 --- a/vendor/github.com/stretchr/objx/Taskfile.yml +++ /dev/null @@ -1,32 +0,0 @@ -default: - deps: [test] - -dl-deps: - desc: Downloads cli dependencies - cmds: - - go get -u github.com/golang/lint/golint - - go get -u github.com/golang/dep/cmd/dep - -update-deps: - desc: Updates dependencies - cmds: - - dep ensure - - dep ensure -update - -lint: - desc: Runs golint - cmds: - - go fmt $(go list ./... | grep -v /vendor/) - - go vet $(go list ./... | grep -v /vendor/) - - golint $(ls *.go | grep -v "doc.go") - silent: true - -test: - desc: Runs go tests - cmds: - - go test -race . - -test-coverage: - desc: Runs go tests and calucates test coverage - cmds: - - go test -coverprofile=c.out . diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go deleted file mode 100644 index 204356a22..000000000 --- a/vendor/github.com/stretchr/objx/accessors.go +++ /dev/null @@ -1,148 +0,0 @@ -package objx - -import ( - "regexp" - "strconv" - "strings" -) - -// arrayAccesRegexString is the regex used to extract the array number -// from the access path -const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` - -// arrayAccesRegex is the compiled arrayAccesRegexString -var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) - -// Get gets the value using the specified selector and -// returns it inside a new Obj object. -// -// If it cannot find the value, Get will return a nil -// value inside an instance of Obj. -// -// Get can only operate directly on map[string]interface{} and []interface. -// -// Example -// -// To access the title of the third chapter of the second book, do: -// -// o.Get("books[1].chapters[2].title") -func (m Map) Get(selector string) *Value { - rawObj := access(m, selector, nil, false) - return &Value{data: rawObj} -} - -// Set sets the value using the specified selector and -// returns the object on which Set was called. -// -// Set can only operate directly on map[string]interface{} and []interface -// -// Example -// -// To set the title of the third chapter of the second book, do: -// -// o.Set("books[1].chapters[2].title","Time to Go") -func (m Map) Set(selector string, value interface{}) Map { - access(m, selector, value, true) - return m -} - -// access accesses the object using the selector and performs the -// appropriate action. -func access(current, selector, value interface{}, isSet bool) interface{} { - switch selector.(type) { - case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: - if array, ok := current.([]interface{}); ok { - index := intFromInterface(selector) - if index >= len(array) { - return nil - } - return array[index] - } - return nil - - case string: - selStr := selector.(string) - selSegs := strings.SplitN(selStr, PathSeparator, 2) - thisSel := selSegs[0] - index := -1 - var err error - - if strings.Contains(thisSel, "[") { - arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel) - if len(arrayMatches) > 0 { - // Get the key into the map - thisSel = arrayMatches[1] - - // Get the index into the array at the key - index, err = strconv.Atoi(arrayMatches[2]) - - if err != nil { - // This should never happen. If it does, something has gone - // seriously wrong. Panic. - panic("objx: Array index is not an integer. Must use array[int].") - } - } - } - if curMap, ok := current.(Map); ok { - current = map[string]interface{}(curMap) - } - // get the object in question - switch current.(type) { - case map[string]interface{}: - curMSI := current.(map[string]interface{}) - if len(selSegs) <= 1 && isSet { - curMSI[thisSel] = value - return nil - } - current = curMSI[thisSel] - default: - current = nil - } - // do we need to access the item of an array? - if index > -1 { - if array, ok := current.([]interface{}); ok { - if index < len(array) { - current = array[index] - } else { - current = nil - } - } - } - if len(selSegs) > 1 { - current = access(current, selSegs[1], value, isSet) - } - } - return current -} - -// intFromInterface converts an interface object to the largest -// representation of an unsigned integer using a type switch and -// assertions -func intFromInterface(selector interface{}) int { - var value int - switch selector.(type) { - case int: - value = selector.(int) - case int8: - value = int(selector.(int8)) - case int16: - value = int(selector.(int16)) - case int32: - value = int(selector.(int32)) - case int64: - value = int(selector.(int64)) - case uint: - value = int(selector.(uint)) - case uint8: - value = int(selector.(uint8)) - case uint16: - value = int(selector.(uint16)) - case uint32: - value = int(selector.(uint32)) - case uint64: - value = int(selector.(uint64)) - default: - return 0 - } - return value -} diff --git a/vendor/github.com/stretchr/objx/constants.go b/vendor/github.com/stretchr/objx/constants.go deleted file mode 100644 index f9eb42a25..000000000 --- a/vendor/github.com/stretchr/objx/constants.go +++ /dev/null @@ -1,13 +0,0 @@ -package objx - -const ( - // PathSeparator is the character used to separate the elements - // of the keypath. - // - // For example, `location.address.city` - PathSeparator string = "." - - // SignatureSeparator is the character that is used to - // separate the Base64 string from the security signature. - SignatureSeparator = "_" -) diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go deleted file mode 100644 index 5e020f310..000000000 --- a/vendor/github.com/stretchr/objx/conversions.go +++ /dev/null @@ -1,108 +0,0 @@ -package objx - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/url" -) - -// JSON converts the contained object to a JSON string -// representation -func (m Map) JSON() (string, error) { - result, err := json.Marshal(m) - if err != nil { - err = errors.New("objx: JSON encode failed with: " + err.Error()) - } - return string(result), err -} - -// MustJSON converts the contained object to a JSON string -// representation and panics if there is an error -func (m Map) MustJSON() string { - result, err := m.JSON() - if err != nil { - panic(err.Error()) - } - return result -} - -// Base64 converts the contained object to a Base64 string -// representation of the JSON string representation -func (m Map) Base64() (string, error) { - var buf bytes.Buffer - - jsonData, err := m.JSON() - if err != nil { - return "", err - } - - encoder := base64.NewEncoder(base64.StdEncoding, &buf) - _, err = encoder.Write([]byte(jsonData)) - if err != nil { - return "", err - } - _ = encoder.Close() - - return buf.String(), nil -} - -// MustBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and panics -// if there is an error -func (m Map) MustBase64() string { - result, err := m.Base64() - if err != nil { - panic(err.Error()) - } - return result -} - -// SignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key. -func (m Map) SignedBase64(key string) (string, error) { - base64, err := m.Base64() - if err != nil { - return "", err - } - - sig := HashWithKey(base64, key) - return base64 + SignatureSeparator + sig, nil -} - -// MustSignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key and panics if there is an error -func (m Map) MustSignedBase64(key string) string { - result, err := m.SignedBase64(key) - if err != nil { - panic(err.Error()) - } - return result -} - -/* - URL Query - ------------------------------------------------ -*/ - -// URLValues creates a url.Values object from an Obj. This -// function requires that the wrapped object be a map[string]interface{} -func (m Map) URLValues() url.Values { - vals := make(url.Values) - for k, v := range m { - //TODO: can this be done without sprintf? - vals.Set(k, fmt.Sprintf("%v", v)) - } - return vals -} - -// URLQuery gets an encoded URL query representing the given -// Obj. This function requires that the wrapped object be a -// map[string]interface{} -func (m Map) URLQuery() (string, error) { - return m.URLValues().Encode(), nil -} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go deleted file mode 100644 index 6d6af1a83..000000000 --- a/vendor/github.com/stretchr/objx/doc.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Objx - Go package for dealing with maps, slices, JSON and other data. - -Overview - -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes -a powerful `Get` method (among others) that allows you to easily and quickly get -access to data within the map, without having to worry too much about type assertions, -missing data, default values etc. - -Pattern - -Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. -Call one of the `objx.` functions to create your `objx.Map` to get going: - - m, err := objx.FromJSON(json) - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, -the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - - m.Get("places[0].latlng") - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - - if m.Get("code").IsStr() { // Your code... } - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - - m.Get("code").Int() - -If there's no value there (or if it's the wrong type) then a default value will be returned, -or you can be explicit about the default value. - - Get("code").Int(-1) - -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, -manipulating and selecting that data. You can find out more by exploring the index below. - -Reading data - -A simple example of how to use Objx: - - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() - - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) - -Ranging - -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. -For example, to `range` the data, do what you would expect: - - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } -*/ -package objx diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go deleted file mode 100644 index 406bc8926..000000000 --- a/vendor/github.com/stretchr/objx/map.go +++ /dev/null @@ -1,190 +0,0 @@ -package objx - -import ( - "encoding/base64" - "encoding/json" - "errors" - "io/ioutil" - "net/url" - "strings" -) - -// MSIConvertable is an interface that defines methods for converting your -// custom types to a map[string]interface{} representation. -type MSIConvertable interface { - // MSI gets a map[string]interface{} (msi) representing the - // object. - MSI() map[string]interface{} -} - -// Map provides extended functionality for working with -// untyped data, in particular map[string]interface (msi). -type Map map[string]interface{} - -// Value returns the internal value instance -func (m Map) Value() *Value { - return &Value{data: m} -} - -// Nil represents a nil Map. -var Nil = New(nil) - -// New creates a new Map containing the map[string]interface{} in the data argument. -// If the data argument is not a map[string]interface, New attempts to call the -// MSI() method on the MSIConvertable interface to create one. -func New(data interface{}) Map { - if _, ok := data.(map[string]interface{}); !ok { - if converter, ok := data.(MSIConvertable); ok { - data = converter.MSI() - } else { - return nil - } - } - return Map(data.(map[string]interface{})) -} - -// MSI creates a map[string]interface{} and puts it inside a new Map. -// -// The arguments follow a key, value pattern. -// -// -// Returns nil if any key argument is non-string or if there are an odd number of arguments. -// -// Example -// -// To easily create Maps: -// -// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) -// -// // creates an Map equivalent to -// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} -func MSI(keyAndValuePairs ...interface{}) Map { - newMap := Map{} - keyAndValuePairsLen := len(keyAndValuePairs) - if keyAndValuePairsLen%2 != 0 { - return nil - } - for i := 0; i < keyAndValuePairsLen; i = i + 2 { - key := keyAndValuePairs[i] - value := keyAndValuePairs[i+1] - - // make sure the key is a string - keyString, keyStringOK := key.(string) - if !keyStringOK { - return nil - } - newMap[keyString] = value - } - return newMap -} - -// ****** Conversion Constructors - -// MustFromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Panics if the JSON is invalid. -func MustFromJSON(jsonString string) Map { - o, err := FromJSON(jsonString) - if err != nil { - panic("objx: MustFromJSON failed with error: " + err.Error()) - } - return o -} - -// FromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Returns an error if the JSON is invalid. -func FromJSON(jsonString string) (Map, error) { - var data interface{} - err := json.Unmarshal([]byte(jsonString), &data) - if err != nil { - return Nil, err - } - return New(data), nil -} - -// FromBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by Base64 -func FromBase64(base64String string) (Map, error) { - decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) - decoded, err := ioutil.ReadAll(decoder) - if err != nil { - return nil, err - } - return FromJSON(string(decoded)) -} - -// MustFromBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromBase64(base64String string) Map { - result, err := FromBase64(base64String) - if err != nil { - panic("objx: MustFromBase64 failed with error: " + err.Error()) - } - return result -} - -// FromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by SignedBase64 -func FromSignedBase64(base64String, key string) (Map, error) { - parts := strings.Split(base64String, SignatureSeparator) - if len(parts) != 2 { - return nil, errors.New("objx: Signed base64 string is malformed") - } - - sig := HashWithKey(parts[0], key) - if parts[1] != sig { - return nil, errors.New("objx: Signature for base64 data does not match") - } - return FromBase64(parts[0]) -} - -// MustFromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromSignedBase64(base64String, key string) Map { - result, err := FromSignedBase64(base64String, key) - if err != nil { - panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) - } - return result -} - -// FromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -func FromURLQuery(query string) (Map, error) { - vals, err := url.ParseQuery(query) - if err != nil { - return nil, err - } - m := Map{} - for k, vals := range vals { - m[k] = vals[0] - } - return m, nil -} - -// MustFromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -// -// Panics if it encounters an error -func MustFromURLQuery(query string) Map { - o, err := FromURLQuery(query) - if err != nil { - panic("objx: MustFromURLQuery failed with error: " + err.Error()) - } - return o -} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go deleted file mode 100644 index c3400a3f7..000000000 --- a/vendor/github.com/stretchr/objx/mutations.go +++ /dev/null @@ -1,77 +0,0 @@ -package objx - -// Exclude returns a new Map with the keys in the specified []string -// excluded. -func (m Map) Exclude(exclude []string) Map { - excluded := make(Map) - for k, v := range m { - if !contains(exclude, k) { - excluded[k] = v - } - } - return excluded -} - -// Copy creates a shallow copy of the Obj. -func (m Map) Copy() Map { - copied := Map{} - for k, v := range m { - copied[k] = v - } - return copied -} - -// Merge blends the specified map with a copy of this map and returns the result. -// -// Keys that appear in both will be selected from the specified map. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) Merge(merge Map) Map { - return m.Copy().MergeHere(merge) -} - -// MergeHere blends the specified map with this map and returns the current map. -// -// Keys that appear in both will be selected from the specified map. The original map -// will be modified. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) MergeHere(merge Map) Map { - for k, v := range merge { - m[k] = v - } - return m -} - -// Transform builds a new Obj giving the transformer a chance -// to change the keys and values as it goes. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { - newMap := Map{} - for k, v := range m { - modifiedKey, modifiedVal := transformer(k, v) - newMap[modifiedKey] = modifiedVal - } - return newMap -} - -// TransformKeys builds a new map using the specified key mapping. -// -// Unspecified keys will be unaltered. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) TransformKeys(mapping map[string]string) Map { - return m.Transform(func(key string, value interface{}) (string, interface{}) { - if newKey, ok := mapping[key]; ok { - return newKey, value - } - return key, value - }) -} - -// Checks if a string slice contains a string -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go deleted file mode 100644 index 692be8e2a..000000000 --- a/vendor/github.com/stretchr/objx/security.go +++ /dev/null @@ -1,12 +0,0 @@ -package objx - -import ( - "crypto/sha1" - "encoding/hex" -) - -// HashWithKey hashes the specified string using the security key -func HashWithKey(data, key string) string { - d := sha1.Sum([]byte(data + ":" + key)) - return hex.EncodeToString(d[:]) -} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go deleted file mode 100644 index d9e0b479a..000000000 --- a/vendor/github.com/stretchr/objx/tests.go +++ /dev/null @@ -1,17 +0,0 @@ -package objx - -// Has gets whether there is something at the specified selector -// or not. -// -// If m is nil, Has will always return false. -func (m Map) Has(selector string) bool { - if m == nil { - return false - } - return !m.Get(selector).IsNil() -} - -// IsNil gets whether the data is nil or not. -func (v *Value) IsNil() bool { - return v == nil || v.data == nil -} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go deleted file mode 100644 index 202a91f8c..000000000 --- a/vendor/github.com/stretchr/objx/type_specific_codegen.go +++ /dev/null @@ -1,2501 +0,0 @@ -package objx - -/* - Inter (interface{} and []interface{}) -*/ - -// Inter gets the value as a interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Inter(optionalDefault ...interface{}) interface{} { - if s, ok := v.data.(interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInter gets the value as a interface{}. -// -// Panics if the object is not a interface{}. -func (v *Value) MustInter() interface{} { - return v.data.(interface{}) -} - -// InterSlice gets the value as a []interface{}, returns the optionalDefault -// value or nil if the value is not a []interface{}. -func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { - if s, ok := v.data.([]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInterSlice gets the value as a []interface{}. -// -// Panics if the object is not a []interface{}. -func (v *Value) MustInterSlice() []interface{} { - return v.data.([]interface{}) -} - -// IsInter gets whether the object contained is a interface{} or not. -func (v *Value) IsInter() bool { - _, ok := v.data.(interface{}) - return ok -} - -// IsInterSlice gets whether the object contained is a []interface{} or not. -func (v *Value) IsInterSlice() bool { - _, ok := v.data.([]interface{}) - return ok -} - -// EachInter calls the specified callback for each object -// in the []interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { - for index, val := range v.MustInterSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInter uses the specified decider function to select items -// from the []interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { - var selected []interface{} - v.EachInter(func(index int, val interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInter uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]interface{}. -func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { - groups := make(map[string][]interface{}) - v.EachInter(func(index int, val interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInter uses the specified function to replace each interface{}s -// by iterating each item. The data in the returned result will be a -// []interface{} containing the replaced items. -func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - replaced := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInter uses the specified collector function to collect a value -// for each of the interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - collected := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - MSI (map[string]interface{} and []map[string]interface{}) -*/ - -// MSI gets the value as a map[string]interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustMSI gets the value as a map[string]interface{}. -// -// Panics if the object is not a map[string]interface{}. -func (v *Value) MustMSI() map[string]interface{} { - return v.data.(map[string]interface{}) -} - -// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault -// value or nil if the value is not a []map[string]interface{}. -func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { - if s, ok := v.data.([]map[string]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustMSISlice gets the value as a []map[string]interface{}. -// -// Panics if the object is not a []map[string]interface{}. -func (v *Value) MustMSISlice() []map[string]interface{} { - return v.data.([]map[string]interface{}) -} - -// IsMSI gets whether the object contained is a map[string]interface{} or not. -func (v *Value) IsMSI() bool { - _, ok := v.data.(map[string]interface{}) - return ok -} - -// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. -func (v *Value) IsMSISlice() bool { - _, ok := v.data.([]map[string]interface{}) - return ok -} - -// EachMSI calls the specified callback for each object -// in the []map[string]interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { - for index, val := range v.MustMSISlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereMSI uses the specified decider function to select items -// from the []map[string]interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { - var selected []map[string]interface{} - v.EachMSI(func(index int, val map[string]interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupMSI uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]map[string]interface{}. -func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { - groups := make(map[string][]map[string]interface{}) - v.EachMSI(func(index int, val map[string]interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]map[string]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceMSI uses the specified function to replace each map[string]interface{}s -// by iterating each item. The data in the returned result will be a -// []map[string]interface{} containing the replaced items. -func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { - arr := v.MustMSISlice() - replaced := make([]map[string]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectMSI uses the specified collector function to collect a value -// for each of the map[string]interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { - arr := v.MustMSISlice() - collected := make([]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - ObjxMap ((Map) and [](Map)) -*/ - -// ObjxMap gets the value as a (Map), returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { - if s, ok := v.data.((Map)); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return New(nil) -} - -// MustObjxMap gets the value as a (Map). -// -// Panics if the object is not a (Map). -func (v *Value) MustObjxMap() Map { - return v.data.((Map)) -} - -// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault -// value or nil if the value is not a [](Map). -func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { - if s, ok := v.data.([](Map)); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustObjxMapSlice gets the value as a [](Map). -// -// Panics if the object is not a [](Map). -func (v *Value) MustObjxMapSlice() [](Map) { - return v.data.([](Map)) -} - -// IsObjxMap gets whether the object contained is a (Map) or not. -func (v *Value) IsObjxMap() bool { - _, ok := v.data.((Map)) - return ok -} - -// IsObjxMapSlice gets whether the object contained is a [](Map) or not. -func (v *Value) IsObjxMapSlice() bool { - _, ok := v.data.([](Map)) - return ok -} - -// EachObjxMap calls the specified callback for each object -// in the [](Map). -// -// Panics if the object is the wrong type. -func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { - for index, val := range v.MustObjxMapSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereObjxMap uses the specified decider function to select items -// from the [](Map). The object contained in the result will contain -// only the selected items. -func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { - var selected [](Map) - v.EachObjxMap(func(index int, val Map) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupObjxMap uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][](Map). -func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { - groups := make(map[string][](Map)) - v.EachObjxMap(func(index int, val Map) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([](Map), 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceObjxMap uses the specified function to replace each (Map)s -// by iterating each item. The data in the returned result will be a -// [](Map) containing the replaced items. -func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { - arr := v.MustObjxMapSlice() - replaced := make([](Map), len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectObjxMap uses the specified collector function to collect a value -// for each of the (Map)s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { - arr := v.MustObjxMapSlice() - collected := make([]interface{}, len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Bool (bool and []bool) -*/ - -// Bool gets the value as a bool, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Bool(optionalDefault ...bool) bool { - if s, ok := v.data.(bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return false -} - -// MustBool gets the value as a bool. -// -// Panics if the object is not a bool. -func (v *Value) MustBool() bool { - return v.data.(bool) -} - -// BoolSlice gets the value as a []bool, returns the optionalDefault -// value or nil if the value is not a []bool. -func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { - if s, ok := v.data.([]bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustBoolSlice gets the value as a []bool. -// -// Panics if the object is not a []bool. -func (v *Value) MustBoolSlice() []bool { - return v.data.([]bool) -} - -// IsBool gets whether the object contained is a bool or not. -func (v *Value) IsBool() bool { - _, ok := v.data.(bool) - return ok -} - -// IsBoolSlice gets whether the object contained is a []bool or not. -func (v *Value) IsBoolSlice() bool { - _, ok := v.data.([]bool) - return ok -} - -// EachBool calls the specified callback for each object -// in the []bool. -// -// Panics if the object is the wrong type. -func (v *Value) EachBool(callback func(int, bool) bool) *Value { - for index, val := range v.MustBoolSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereBool uses the specified decider function to select items -// from the []bool. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereBool(decider func(int, bool) bool) *Value { - var selected []bool - v.EachBool(func(index int, val bool) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupBool uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]bool. -func (v *Value) GroupBool(grouper func(int, bool) string) *Value { - groups := make(map[string][]bool) - v.EachBool(func(index int, val bool) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]bool, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceBool uses the specified function to replace each bools -// by iterating each item. The data in the returned result will be a -// []bool containing the replaced items. -func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { - arr := v.MustBoolSlice() - replaced := make([]bool, len(arr)) - v.EachBool(func(index int, val bool) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectBool uses the specified collector function to collect a value -// for each of the bools in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { - arr := v.MustBoolSlice() - collected := make([]interface{}, len(arr)) - v.EachBool(func(index int, val bool) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Str (string and []string) -*/ - -// Str gets the value as a string, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Str(optionalDefault ...string) string { - if s, ok := v.data.(string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return "" -} - -// MustStr gets the value as a string. -// -// Panics if the object is not a string. -func (v *Value) MustStr() string { - return v.data.(string) -} - -// StrSlice gets the value as a []string, returns the optionalDefault -// value or nil if the value is not a []string. -func (v *Value) StrSlice(optionalDefault ...[]string) []string { - if s, ok := v.data.([]string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustStrSlice gets the value as a []string. -// -// Panics if the object is not a []string. -func (v *Value) MustStrSlice() []string { - return v.data.([]string) -} - -// IsStr gets whether the object contained is a string or not. -func (v *Value) IsStr() bool { - _, ok := v.data.(string) - return ok -} - -// IsStrSlice gets whether the object contained is a []string or not. -func (v *Value) IsStrSlice() bool { - _, ok := v.data.([]string) - return ok -} - -// EachStr calls the specified callback for each object -// in the []string. -// -// Panics if the object is the wrong type. -func (v *Value) EachStr(callback func(int, string) bool) *Value { - for index, val := range v.MustStrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereStr uses the specified decider function to select items -// from the []string. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereStr(decider func(int, string) bool) *Value { - var selected []string - v.EachStr(func(index int, val string) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupStr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]string. -func (v *Value) GroupStr(grouper func(int, string) string) *Value { - groups := make(map[string][]string) - v.EachStr(func(index int, val string) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]string, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceStr uses the specified function to replace each strings -// by iterating each item. The data in the returned result will be a -// []string containing the replaced items. -func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { - arr := v.MustStrSlice() - replaced := make([]string, len(arr)) - v.EachStr(func(index int, val string) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectStr uses the specified collector function to collect a value -// for each of the strings in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { - arr := v.MustStrSlice() - collected := make([]interface{}, len(arr)) - v.EachStr(func(index int, val string) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int (int and []int) -*/ - -// Int gets the value as a int, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int(optionalDefault ...int) int { - if s, ok := v.data.(int); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt gets the value as a int. -// -// Panics if the object is not a int. -func (v *Value) MustInt() int { - return v.data.(int) -} - -// IntSlice gets the value as a []int, returns the optionalDefault -// value or nil if the value is not a []int. -func (v *Value) IntSlice(optionalDefault ...[]int) []int { - if s, ok := v.data.([]int); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustIntSlice gets the value as a []int. -// -// Panics if the object is not a []int. -func (v *Value) MustIntSlice() []int { - return v.data.([]int) -} - -// IsInt gets whether the object contained is a int or not. -func (v *Value) IsInt() bool { - _, ok := v.data.(int) - return ok -} - -// IsIntSlice gets whether the object contained is a []int or not. -func (v *Value) IsIntSlice() bool { - _, ok := v.data.([]int) - return ok -} - -// EachInt calls the specified callback for each object -// in the []int. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt(callback func(int, int) bool) *Value { - for index, val := range v.MustIntSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt uses the specified decider function to select items -// from the []int. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt(decider func(int, int) bool) *Value { - var selected []int - v.EachInt(func(index int, val int) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int. -func (v *Value) GroupInt(grouper func(int, int) string) *Value { - groups := make(map[string][]int) - v.EachInt(func(index int, val int) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt uses the specified function to replace each ints -// by iterating each item. The data in the returned result will be a -// []int containing the replaced items. -func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { - arr := v.MustIntSlice() - replaced := make([]int, len(arr)) - v.EachInt(func(index int, val int) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt uses the specified collector function to collect a value -// for each of the ints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { - arr := v.MustIntSlice() - collected := make([]interface{}, len(arr)) - v.EachInt(func(index int, val int) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int8 (int8 and []int8) -*/ - -// Int8 gets the value as a int8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int8(optionalDefault ...int8) int8 { - if s, ok := v.data.(int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt8 gets the value as a int8. -// -// Panics if the object is not a int8. -func (v *Value) MustInt8() int8 { - return v.data.(int8) -} - -// Int8Slice gets the value as a []int8, returns the optionalDefault -// value or nil if the value is not a []int8. -func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { - if s, ok := v.data.([]int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt8Slice gets the value as a []int8. -// -// Panics if the object is not a []int8. -func (v *Value) MustInt8Slice() []int8 { - return v.data.([]int8) -} - -// IsInt8 gets whether the object contained is a int8 or not. -func (v *Value) IsInt8() bool { - _, ok := v.data.(int8) - return ok -} - -// IsInt8Slice gets whether the object contained is a []int8 or not. -func (v *Value) IsInt8Slice() bool { - _, ok := v.data.([]int8) - return ok -} - -// EachInt8 calls the specified callback for each object -// in the []int8. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt8(callback func(int, int8) bool) *Value { - for index, val := range v.MustInt8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt8 uses the specified decider function to select items -// from the []int8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { - var selected []int8 - v.EachInt8(func(index int, val int8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int8. -func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { - groups := make(map[string][]int8) - v.EachInt8(func(index int, val int8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt8 uses the specified function to replace each int8s -// by iterating each item. The data in the returned result will be a -// []int8 containing the replaced items. -func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { - arr := v.MustInt8Slice() - replaced := make([]int8, len(arr)) - v.EachInt8(func(index int, val int8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt8 uses the specified collector function to collect a value -// for each of the int8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { - arr := v.MustInt8Slice() - collected := make([]interface{}, len(arr)) - v.EachInt8(func(index int, val int8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int16 (int16 and []int16) -*/ - -// Int16 gets the value as a int16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int16(optionalDefault ...int16) int16 { - if s, ok := v.data.(int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt16 gets the value as a int16. -// -// Panics if the object is not a int16. -func (v *Value) MustInt16() int16 { - return v.data.(int16) -} - -// Int16Slice gets the value as a []int16, returns the optionalDefault -// value or nil if the value is not a []int16. -func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { - if s, ok := v.data.([]int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt16Slice gets the value as a []int16. -// -// Panics if the object is not a []int16. -func (v *Value) MustInt16Slice() []int16 { - return v.data.([]int16) -} - -// IsInt16 gets whether the object contained is a int16 or not. -func (v *Value) IsInt16() bool { - _, ok := v.data.(int16) - return ok -} - -// IsInt16Slice gets whether the object contained is a []int16 or not. -func (v *Value) IsInt16Slice() bool { - _, ok := v.data.([]int16) - return ok -} - -// EachInt16 calls the specified callback for each object -// in the []int16. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt16(callback func(int, int16) bool) *Value { - for index, val := range v.MustInt16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt16 uses the specified decider function to select items -// from the []int16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { - var selected []int16 - v.EachInt16(func(index int, val int16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int16. -func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { - groups := make(map[string][]int16) - v.EachInt16(func(index int, val int16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt16 uses the specified function to replace each int16s -// by iterating each item. The data in the returned result will be a -// []int16 containing the replaced items. -func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { - arr := v.MustInt16Slice() - replaced := make([]int16, len(arr)) - v.EachInt16(func(index int, val int16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt16 uses the specified collector function to collect a value -// for each of the int16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { - arr := v.MustInt16Slice() - collected := make([]interface{}, len(arr)) - v.EachInt16(func(index int, val int16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int32 (int32 and []int32) -*/ - -// Int32 gets the value as a int32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int32(optionalDefault ...int32) int32 { - if s, ok := v.data.(int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt32 gets the value as a int32. -// -// Panics if the object is not a int32. -func (v *Value) MustInt32() int32 { - return v.data.(int32) -} - -// Int32Slice gets the value as a []int32, returns the optionalDefault -// value or nil if the value is not a []int32. -func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { - if s, ok := v.data.([]int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt32Slice gets the value as a []int32. -// -// Panics if the object is not a []int32. -func (v *Value) MustInt32Slice() []int32 { - return v.data.([]int32) -} - -// IsInt32 gets whether the object contained is a int32 or not. -func (v *Value) IsInt32() bool { - _, ok := v.data.(int32) - return ok -} - -// IsInt32Slice gets whether the object contained is a []int32 or not. -func (v *Value) IsInt32Slice() bool { - _, ok := v.data.([]int32) - return ok -} - -// EachInt32 calls the specified callback for each object -// in the []int32. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt32(callback func(int, int32) bool) *Value { - for index, val := range v.MustInt32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt32 uses the specified decider function to select items -// from the []int32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { - var selected []int32 - v.EachInt32(func(index int, val int32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int32. -func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { - groups := make(map[string][]int32) - v.EachInt32(func(index int, val int32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt32 uses the specified function to replace each int32s -// by iterating each item. The data in the returned result will be a -// []int32 containing the replaced items. -func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { - arr := v.MustInt32Slice() - replaced := make([]int32, len(arr)) - v.EachInt32(func(index int, val int32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt32 uses the specified collector function to collect a value -// for each of the int32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { - arr := v.MustInt32Slice() - collected := make([]interface{}, len(arr)) - v.EachInt32(func(index int, val int32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int64 (int64 and []int64) -*/ - -// Int64 gets the value as a int64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int64(optionalDefault ...int64) int64 { - if s, ok := v.data.(int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt64 gets the value as a int64. -// -// Panics if the object is not a int64. -func (v *Value) MustInt64() int64 { - return v.data.(int64) -} - -// Int64Slice gets the value as a []int64, returns the optionalDefault -// value or nil if the value is not a []int64. -func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { - if s, ok := v.data.([]int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt64Slice gets the value as a []int64. -// -// Panics if the object is not a []int64. -func (v *Value) MustInt64Slice() []int64 { - return v.data.([]int64) -} - -// IsInt64 gets whether the object contained is a int64 or not. -func (v *Value) IsInt64() bool { - _, ok := v.data.(int64) - return ok -} - -// IsInt64Slice gets whether the object contained is a []int64 or not. -func (v *Value) IsInt64Slice() bool { - _, ok := v.data.([]int64) - return ok -} - -// EachInt64 calls the specified callback for each object -// in the []int64. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt64(callback func(int, int64) bool) *Value { - for index, val := range v.MustInt64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt64 uses the specified decider function to select items -// from the []int64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { - var selected []int64 - v.EachInt64(func(index int, val int64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int64. -func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { - groups := make(map[string][]int64) - v.EachInt64(func(index int, val int64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt64 uses the specified function to replace each int64s -// by iterating each item. The data in the returned result will be a -// []int64 containing the replaced items. -func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { - arr := v.MustInt64Slice() - replaced := make([]int64, len(arr)) - v.EachInt64(func(index int, val int64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt64 uses the specified collector function to collect a value -// for each of the int64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { - arr := v.MustInt64Slice() - collected := make([]interface{}, len(arr)) - v.EachInt64(func(index int, val int64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint (uint and []uint) -*/ - -// Uint gets the value as a uint, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint(optionalDefault ...uint) uint { - if s, ok := v.data.(uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint gets the value as a uint. -// -// Panics if the object is not a uint. -func (v *Value) MustUint() uint { - return v.data.(uint) -} - -// UintSlice gets the value as a []uint, returns the optionalDefault -// value or nil if the value is not a []uint. -func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { - if s, ok := v.data.([]uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintSlice gets the value as a []uint. -// -// Panics if the object is not a []uint. -func (v *Value) MustUintSlice() []uint { - return v.data.([]uint) -} - -// IsUint gets whether the object contained is a uint or not. -func (v *Value) IsUint() bool { - _, ok := v.data.(uint) - return ok -} - -// IsUintSlice gets whether the object contained is a []uint or not. -func (v *Value) IsUintSlice() bool { - _, ok := v.data.([]uint) - return ok -} - -// EachUint calls the specified callback for each object -// in the []uint. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint(callback func(int, uint) bool) *Value { - for index, val := range v.MustUintSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint uses the specified decider function to select items -// from the []uint. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint(decider func(int, uint) bool) *Value { - var selected []uint - v.EachUint(func(index int, val uint) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint. -func (v *Value) GroupUint(grouper func(int, uint) string) *Value { - groups := make(map[string][]uint) - v.EachUint(func(index int, val uint) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint uses the specified function to replace each uints -// by iterating each item. The data in the returned result will be a -// []uint containing the replaced items. -func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { - arr := v.MustUintSlice() - replaced := make([]uint, len(arr)) - v.EachUint(func(index int, val uint) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint uses the specified collector function to collect a value -// for each of the uints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { - arr := v.MustUintSlice() - collected := make([]interface{}, len(arr)) - v.EachUint(func(index int, val uint) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint8 (uint8 and []uint8) -*/ - -// Uint8 gets the value as a uint8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint8(optionalDefault ...uint8) uint8 { - if s, ok := v.data.(uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint8 gets the value as a uint8. -// -// Panics if the object is not a uint8. -func (v *Value) MustUint8() uint8 { - return v.data.(uint8) -} - -// Uint8Slice gets the value as a []uint8, returns the optionalDefault -// value or nil if the value is not a []uint8. -func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { - if s, ok := v.data.([]uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint8Slice gets the value as a []uint8. -// -// Panics if the object is not a []uint8. -func (v *Value) MustUint8Slice() []uint8 { - return v.data.([]uint8) -} - -// IsUint8 gets whether the object contained is a uint8 or not. -func (v *Value) IsUint8() bool { - _, ok := v.data.(uint8) - return ok -} - -// IsUint8Slice gets whether the object contained is a []uint8 or not. -func (v *Value) IsUint8Slice() bool { - _, ok := v.data.([]uint8) - return ok -} - -// EachUint8 calls the specified callback for each object -// in the []uint8. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { - for index, val := range v.MustUint8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint8 uses the specified decider function to select items -// from the []uint8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { - var selected []uint8 - v.EachUint8(func(index int, val uint8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint8. -func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { - groups := make(map[string][]uint8) - v.EachUint8(func(index int, val uint8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint8 uses the specified function to replace each uint8s -// by iterating each item. The data in the returned result will be a -// []uint8 containing the replaced items. -func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { - arr := v.MustUint8Slice() - replaced := make([]uint8, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint8 uses the specified collector function to collect a value -// for each of the uint8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { - arr := v.MustUint8Slice() - collected := make([]interface{}, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint16 (uint16 and []uint16) -*/ - -// Uint16 gets the value as a uint16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint16(optionalDefault ...uint16) uint16 { - if s, ok := v.data.(uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint16 gets the value as a uint16. -// -// Panics if the object is not a uint16. -func (v *Value) MustUint16() uint16 { - return v.data.(uint16) -} - -// Uint16Slice gets the value as a []uint16, returns the optionalDefault -// value or nil if the value is not a []uint16. -func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { - if s, ok := v.data.([]uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint16Slice gets the value as a []uint16. -// -// Panics if the object is not a []uint16. -func (v *Value) MustUint16Slice() []uint16 { - return v.data.([]uint16) -} - -// IsUint16 gets whether the object contained is a uint16 or not. -func (v *Value) IsUint16() bool { - _, ok := v.data.(uint16) - return ok -} - -// IsUint16Slice gets whether the object contained is a []uint16 or not. -func (v *Value) IsUint16Slice() bool { - _, ok := v.data.([]uint16) - return ok -} - -// EachUint16 calls the specified callback for each object -// in the []uint16. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { - for index, val := range v.MustUint16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint16 uses the specified decider function to select items -// from the []uint16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { - var selected []uint16 - v.EachUint16(func(index int, val uint16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint16. -func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { - groups := make(map[string][]uint16) - v.EachUint16(func(index int, val uint16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint16 uses the specified function to replace each uint16s -// by iterating each item. The data in the returned result will be a -// []uint16 containing the replaced items. -func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { - arr := v.MustUint16Slice() - replaced := make([]uint16, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint16 uses the specified collector function to collect a value -// for each of the uint16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { - arr := v.MustUint16Slice() - collected := make([]interface{}, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint32 (uint32 and []uint32) -*/ - -// Uint32 gets the value as a uint32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint32(optionalDefault ...uint32) uint32 { - if s, ok := v.data.(uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint32 gets the value as a uint32. -// -// Panics if the object is not a uint32. -func (v *Value) MustUint32() uint32 { - return v.data.(uint32) -} - -// Uint32Slice gets the value as a []uint32, returns the optionalDefault -// value or nil if the value is not a []uint32. -func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { - if s, ok := v.data.([]uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint32Slice gets the value as a []uint32. -// -// Panics if the object is not a []uint32. -func (v *Value) MustUint32Slice() []uint32 { - return v.data.([]uint32) -} - -// IsUint32 gets whether the object contained is a uint32 or not. -func (v *Value) IsUint32() bool { - _, ok := v.data.(uint32) - return ok -} - -// IsUint32Slice gets whether the object contained is a []uint32 or not. -func (v *Value) IsUint32Slice() bool { - _, ok := v.data.([]uint32) - return ok -} - -// EachUint32 calls the specified callback for each object -// in the []uint32. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { - for index, val := range v.MustUint32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint32 uses the specified decider function to select items -// from the []uint32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { - var selected []uint32 - v.EachUint32(func(index int, val uint32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint32. -func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { - groups := make(map[string][]uint32) - v.EachUint32(func(index int, val uint32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint32 uses the specified function to replace each uint32s -// by iterating each item. The data in the returned result will be a -// []uint32 containing the replaced items. -func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { - arr := v.MustUint32Slice() - replaced := make([]uint32, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint32 uses the specified collector function to collect a value -// for each of the uint32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { - arr := v.MustUint32Slice() - collected := make([]interface{}, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint64 (uint64 and []uint64) -*/ - -// Uint64 gets the value as a uint64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint64(optionalDefault ...uint64) uint64 { - if s, ok := v.data.(uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint64 gets the value as a uint64. -// -// Panics if the object is not a uint64. -func (v *Value) MustUint64() uint64 { - return v.data.(uint64) -} - -// Uint64Slice gets the value as a []uint64, returns the optionalDefault -// value or nil if the value is not a []uint64. -func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { - if s, ok := v.data.([]uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint64Slice gets the value as a []uint64. -// -// Panics if the object is not a []uint64. -func (v *Value) MustUint64Slice() []uint64 { - return v.data.([]uint64) -} - -// IsUint64 gets whether the object contained is a uint64 or not. -func (v *Value) IsUint64() bool { - _, ok := v.data.(uint64) - return ok -} - -// IsUint64Slice gets whether the object contained is a []uint64 or not. -func (v *Value) IsUint64Slice() bool { - _, ok := v.data.([]uint64) - return ok -} - -// EachUint64 calls the specified callback for each object -// in the []uint64. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { - for index, val := range v.MustUint64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint64 uses the specified decider function to select items -// from the []uint64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { - var selected []uint64 - v.EachUint64(func(index int, val uint64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint64. -func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { - groups := make(map[string][]uint64) - v.EachUint64(func(index int, val uint64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint64 uses the specified function to replace each uint64s -// by iterating each item. The data in the returned result will be a -// []uint64 containing the replaced items. -func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { - arr := v.MustUint64Slice() - replaced := make([]uint64, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint64 uses the specified collector function to collect a value -// for each of the uint64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { - arr := v.MustUint64Slice() - collected := make([]interface{}, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uintptr (uintptr and []uintptr) -*/ - -// Uintptr gets the value as a uintptr, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { - if s, ok := v.data.(uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUintptr gets the value as a uintptr. -// -// Panics if the object is not a uintptr. -func (v *Value) MustUintptr() uintptr { - return v.data.(uintptr) -} - -// UintptrSlice gets the value as a []uintptr, returns the optionalDefault -// value or nil if the value is not a []uintptr. -func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { - if s, ok := v.data.([]uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintptrSlice gets the value as a []uintptr. -// -// Panics if the object is not a []uintptr. -func (v *Value) MustUintptrSlice() []uintptr { - return v.data.([]uintptr) -} - -// IsUintptr gets whether the object contained is a uintptr or not. -func (v *Value) IsUintptr() bool { - _, ok := v.data.(uintptr) - return ok -} - -// IsUintptrSlice gets whether the object contained is a []uintptr or not. -func (v *Value) IsUintptrSlice() bool { - _, ok := v.data.([]uintptr) - return ok -} - -// EachUintptr calls the specified callback for each object -// in the []uintptr. -// -// Panics if the object is the wrong type. -func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { - for index, val := range v.MustUintptrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUintptr uses the specified decider function to select items -// from the []uintptr. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { - var selected []uintptr - v.EachUintptr(func(index int, val uintptr) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUintptr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uintptr. -func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { - groups := make(map[string][]uintptr) - v.EachUintptr(func(index int, val uintptr) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uintptr, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUintptr uses the specified function to replace each uintptrs -// by iterating each item. The data in the returned result will be a -// []uintptr containing the replaced items. -func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { - arr := v.MustUintptrSlice() - replaced := make([]uintptr, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUintptr uses the specified collector function to collect a value -// for each of the uintptrs in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { - arr := v.MustUintptrSlice() - collected := make([]interface{}, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float32 (float32 and []float32) -*/ - -// Float32 gets the value as a float32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float32(optionalDefault ...float32) float32 { - if s, ok := v.data.(float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat32 gets the value as a float32. -// -// Panics if the object is not a float32. -func (v *Value) MustFloat32() float32 { - return v.data.(float32) -} - -// Float32Slice gets the value as a []float32, returns the optionalDefault -// value or nil if the value is not a []float32. -func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { - if s, ok := v.data.([]float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat32Slice gets the value as a []float32. -// -// Panics if the object is not a []float32. -func (v *Value) MustFloat32Slice() []float32 { - return v.data.([]float32) -} - -// IsFloat32 gets whether the object contained is a float32 or not. -func (v *Value) IsFloat32() bool { - _, ok := v.data.(float32) - return ok -} - -// IsFloat32Slice gets whether the object contained is a []float32 or not. -func (v *Value) IsFloat32Slice() bool { - _, ok := v.data.([]float32) - return ok -} - -// EachFloat32 calls the specified callback for each object -// in the []float32. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { - for index, val := range v.MustFloat32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat32 uses the specified decider function to select items -// from the []float32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { - var selected []float32 - v.EachFloat32(func(index int, val float32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float32. -func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { - groups := make(map[string][]float32) - v.EachFloat32(func(index int, val float32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat32 uses the specified function to replace each float32s -// by iterating each item. The data in the returned result will be a -// []float32 containing the replaced items. -func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { - arr := v.MustFloat32Slice() - replaced := make([]float32, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat32 uses the specified collector function to collect a value -// for each of the float32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { - arr := v.MustFloat32Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float64 (float64 and []float64) -*/ - -// Float64 gets the value as a float64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float64(optionalDefault ...float64) float64 { - if s, ok := v.data.(float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat64 gets the value as a float64. -// -// Panics if the object is not a float64. -func (v *Value) MustFloat64() float64 { - return v.data.(float64) -} - -// Float64Slice gets the value as a []float64, returns the optionalDefault -// value or nil if the value is not a []float64. -func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { - if s, ok := v.data.([]float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat64Slice gets the value as a []float64. -// -// Panics if the object is not a []float64. -func (v *Value) MustFloat64Slice() []float64 { - return v.data.([]float64) -} - -// IsFloat64 gets whether the object contained is a float64 or not. -func (v *Value) IsFloat64() bool { - _, ok := v.data.(float64) - return ok -} - -// IsFloat64Slice gets whether the object contained is a []float64 or not. -func (v *Value) IsFloat64Slice() bool { - _, ok := v.data.([]float64) - return ok -} - -// EachFloat64 calls the specified callback for each object -// in the []float64. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { - for index, val := range v.MustFloat64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat64 uses the specified decider function to select items -// from the []float64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { - var selected []float64 - v.EachFloat64(func(index int, val float64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float64. -func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { - groups := make(map[string][]float64) - v.EachFloat64(func(index int, val float64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat64 uses the specified function to replace each float64s -// by iterating each item. The data in the returned result will be a -// []float64 containing the replaced items. -func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { - arr := v.MustFloat64Slice() - replaced := make([]float64, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat64 uses the specified collector function to collect a value -// for each of the float64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { - arr := v.MustFloat64Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex64 (complex64 and []complex64) -*/ - -// Complex64 gets the value as a complex64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex64(optionalDefault ...complex64) complex64 { - if s, ok := v.data.(complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex64 gets the value as a complex64. -// -// Panics if the object is not a complex64. -func (v *Value) MustComplex64() complex64 { - return v.data.(complex64) -} - -// Complex64Slice gets the value as a []complex64, returns the optionalDefault -// value or nil if the value is not a []complex64. -func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { - if s, ok := v.data.([]complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex64Slice gets the value as a []complex64. -// -// Panics if the object is not a []complex64. -func (v *Value) MustComplex64Slice() []complex64 { - return v.data.([]complex64) -} - -// IsComplex64 gets whether the object contained is a complex64 or not. -func (v *Value) IsComplex64() bool { - _, ok := v.data.(complex64) - return ok -} - -// IsComplex64Slice gets whether the object contained is a []complex64 or not. -func (v *Value) IsComplex64Slice() bool { - _, ok := v.data.([]complex64) - return ok -} - -// EachComplex64 calls the specified callback for each object -// in the []complex64. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { - for index, val := range v.MustComplex64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex64 uses the specified decider function to select items -// from the []complex64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { - var selected []complex64 - v.EachComplex64(func(index int, val complex64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex64. -func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { - groups := make(map[string][]complex64) - v.EachComplex64(func(index int, val complex64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex64 uses the specified function to replace each complex64s -// by iterating each item. The data in the returned result will be a -// []complex64 containing the replaced items. -func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { - arr := v.MustComplex64Slice() - replaced := make([]complex64, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex64 uses the specified collector function to collect a value -// for each of the complex64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { - arr := v.MustComplex64Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex128 (complex128 and []complex128) -*/ - -// Complex128 gets the value as a complex128, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex128(optionalDefault ...complex128) complex128 { - if s, ok := v.data.(complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex128 gets the value as a complex128. -// -// Panics if the object is not a complex128. -func (v *Value) MustComplex128() complex128 { - return v.data.(complex128) -} - -// Complex128Slice gets the value as a []complex128, returns the optionalDefault -// value or nil if the value is not a []complex128. -func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { - if s, ok := v.data.([]complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex128Slice gets the value as a []complex128. -// -// Panics if the object is not a []complex128. -func (v *Value) MustComplex128Slice() []complex128 { - return v.data.([]complex128) -} - -// IsComplex128 gets whether the object contained is a complex128 or not. -func (v *Value) IsComplex128() bool { - _, ok := v.data.(complex128) - return ok -} - -// IsComplex128Slice gets whether the object contained is a []complex128 or not. -func (v *Value) IsComplex128Slice() bool { - _, ok := v.data.([]complex128) - return ok -} - -// EachComplex128 calls the specified callback for each object -// in the []complex128. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { - for index, val := range v.MustComplex128Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex128 uses the specified decider function to select items -// from the []complex128. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { - var selected []complex128 - v.EachComplex128(func(index int, val complex128) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex128 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex128. -func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { - groups := make(map[string][]complex128) - v.EachComplex128(func(index int, val complex128) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex128, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex128 uses the specified function to replace each complex128s -// by iterating each item. The data in the returned result will be a -// []complex128 containing the replaced items. -func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { - arr := v.MustComplex128Slice() - replaced := make([]complex128, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex128 uses the specified collector function to collect a value -// for each of the complex128s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { - arr := v.MustComplex128Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go deleted file mode 100644 index e4b4a1433..000000000 --- a/vendor/github.com/stretchr/objx/value.go +++ /dev/null @@ -1,53 +0,0 @@ -package objx - -import ( - "fmt" - "strconv" -) - -// Value provides methods for extracting interface{} data in various -// types. -type Value struct { - // data contains the raw data being managed by this Value - data interface{} -} - -// Data returns the raw data contained by this Value -func (v *Value) Data() interface{} { - return v.data -} - -// String returns the value always as a string -func (v *Value) String() string { - switch { - case v.IsStr(): - return v.Str() - case v.IsBool(): - return strconv.FormatBool(v.Bool()) - case v.IsFloat32(): - return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) - case v.IsFloat64(): - return strconv.FormatFloat(v.Float64(), 'f', -1, 64) - case v.IsInt(): - return strconv.FormatInt(int64(v.Int()), 10) - case v.IsInt8(): - return strconv.FormatInt(int64(v.Int8()), 10) - case v.IsInt16(): - return strconv.FormatInt(int64(v.Int16()), 10) - case v.IsInt32(): - return strconv.FormatInt(int64(v.Int32()), 10) - case v.IsInt64(): - return strconv.FormatInt(v.Int64(), 10) - case v.IsUint(): - return strconv.FormatUint(uint64(v.Uint()), 10) - case v.IsUint8(): - return strconv.FormatUint(uint64(v.Uint8()), 10) - case v.IsUint16(): - return strconv.FormatUint(uint64(v.Uint16()), 10) - case v.IsUint32(): - return strconv.FormatUint(uint64(v.Uint32()), 10) - case v.IsUint64(): - return strconv.FormatUint(v.Uint64(), 10) - } - return fmt.Sprintf("%#v", v.Data()) -} diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go deleted file mode 100644 index 7324128ef..000000000 --- a/vendor/github.com/stretchr/testify/mock/doc.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package mock provides a system by which it is possible to mock your objects -// and verify calls are happening as expected. -// -// Example Usage -// -// The mock package provides an object, Mock, that tracks activity on another object. It is usually -// embedded into a test object as shown below: -// -// type MyTestObject struct { -// // add a Mock object instance -// mock.Mock -// -// // other fields go here as normal -// } -// -// When implementing the methods of an interface, you wire your functions up -// to call the Mock.Called(args...) method, and return the appropriate values. -// -// For example, to mock a method that saves the name and age of a person and returns -// the year of their birth or an error, you might write this: -// -// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { -// args := o.Called(firstname, lastname, age) -// return args.Int(0), args.Error(1) -// } -// -// The Int, Error and Bool methods are examples of strongly typed getters that take the argument -// index position. Given this argument list: -// -// (12, true, "Something") -// -// You could read them out strongly typed like this: -// -// args.Int(0) -// args.Bool(1) -// args.String(2) -// -// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: -// -// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) -// -// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those -// cases you should check for nil first. -package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go deleted file mode 100644 index 58e0798da..000000000 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ /dev/null @@ -1,917 +0,0 @@ -package mock - -import ( - "errors" - "fmt" - "reflect" - "regexp" - "runtime" - "strings" - "sync" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" - "github.com/stretchr/objx" - "github.com/stretchr/testify/assert" -) - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Logf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - FailNow() -} - -/* - Call -*/ - -// Call represents a method call and is used for setting expectations, -// as well as recording activity. -type Call struct { - Parent *Mock - - // The name of the method that was or will be called. - Method string - - // Holds the arguments of the method. - Arguments Arguments - - // Holds the arguments that should be returned when - // this method is called. - ReturnArguments Arguments - - // Holds the caller info for the On() call - callerInfo []string - - // The number of times to return the return arguments when setting - // expectations. 0 means to always return the value. - Repeatability int - - // Amount of times this call has been called - totalCalls int - - // Call to this method can be optional - optional bool - - // Holds a channel that will be used to block the Return until it either - // receives a message or is closed. nil means it returns immediately. - WaitFor <-chan time.Time - - waitTime time.Duration - - // Holds a handler used to manipulate arguments content that are passed by - // reference. It's useful when mocking methods such as unmarshalers or - // decoders. - RunFn func(Arguments) -} - -func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { - return &Call{ - Parent: parent, - Method: methodName, - Arguments: methodArguments, - ReturnArguments: make([]interface{}, 0), - callerInfo: callerInfo, - Repeatability: 0, - WaitFor: nil, - RunFn: nil, - } -} - -func (c *Call) lock() { - c.Parent.mutex.Lock() -} - -func (c *Call) unlock() { - c.Parent.mutex.Unlock() -} - -// Return specifies the return arguments for the expectation. -// -// Mock.On("DoSomething").Return(errors.New("failed")) -func (c *Call) Return(returnArguments ...interface{}) *Call { - c.lock() - defer c.unlock() - - c.ReturnArguments = returnArguments - - return c -} - -// Once indicates that that the mock should only return the value once. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() -func (c *Call) Once() *Call { - return c.Times(1) -} - -// Twice indicates that that the mock should only return the value twice. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() -func (c *Call) Twice() *Call { - return c.Times(2) -} - -// Times indicates that that the mock should only return the indicated number -// of times. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) -func (c *Call) Times(i int) *Call { - c.lock() - defer c.unlock() - c.Repeatability = i - return c -} - -// WaitUntil sets the channel that will block the mock's return until its closed -// or a message is received. -// -// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) -func (c *Call) WaitUntil(w <-chan time.Time) *Call { - c.lock() - defer c.unlock() - c.WaitFor = w - return c -} - -// After sets how long to block until the call returns -// -// Mock.On("MyMethod", arg1, arg2).After(time.Second) -func (c *Call) After(d time.Duration) *Call { - c.lock() - defer c.unlock() - c.waitTime = d - return c -} - -// Run sets a handler to be called before returning. It can be used when -// mocking a method (such as an unmarshaler) that takes a pointer to a struct and -// sets properties in such struct -// -// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(func(args Arguments) { -// arg := args.Get(0).(*map[string]interface{}) -// arg["foo"] = "bar" -// }) -func (c *Call) Run(fn func(args Arguments)) *Call { - c.lock() - defer c.unlock() - c.RunFn = fn - return c -} - -// Maybe allows the method call to be optional. Not calling an optional method -// will not cause an error while asserting expectations -func (c *Call) Maybe() *Call { - c.lock() - defer c.unlock() - c.optional = true - return c -} - -// On chains a new expectation description onto the mocked interface. This -// allows syntax like. -// -// Mock. -// On("MyMethod", 1).Return(nil). -// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) -//go:noinline -func (c *Call) On(methodName string, arguments ...interface{}) *Call { - return c.Parent.On(methodName, arguments...) -} - -// Mock is the workhorse used to track activity on another object. -// For an example of its usage, refer to the "Example Usage" section at the top -// of this document. -type Mock struct { - // Represents the calls that are expected of - // an object. - ExpectedCalls []*Call - - // Holds the calls that were made to this mocked object. - Calls []Call - - // test is An optional variable that holds the test struct, to be used when an - // invalid mock call was made. - test TestingT - - // TestData holds any data that might be useful for testing. Testify ignores - // this data completely allowing you to do whatever you like with it. - testData objx.Map - - mutex sync.Mutex -} - -// TestData holds any data that might be useful for testing. Testify ignores -// this data completely allowing you to do whatever you like with it. -func (m *Mock) TestData() objx.Map { - - if m.testData == nil { - m.testData = make(objx.Map) - } - - return m.testData -} - -/* - Setting expectations -*/ - -// Test sets the test struct variable of the mock object -func (m *Mock) Test(t TestingT) { - m.mutex.Lock() - defer m.mutex.Unlock() - m.test = t -} - -// fail fails the current test with the given formatted format and args. -// In case that a test was defined, it uses the test APIs for failing a test, -// otherwise it uses panic. -func (m *Mock) fail(format string, args ...interface{}) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.test == nil { - panic(fmt.Sprintf(format, args...)) - } - m.test.Errorf(format, args...) - m.test.FailNow() -} - -// On starts a description of an expectation of the specified method -// being called. -// -// Mock.On("MyMethod", arg1, arg2) -func (m *Mock) On(methodName string, arguments ...interface{}) *Call { - for _, arg := range arguments { - if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { - panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) - } - } - - m.mutex.Lock() - defer m.mutex.Unlock() - c := newCall(m, methodName, assert.CallerInfo(), arguments...) - m.ExpectedCalls = append(m.ExpectedCalls, c) - return c -} - -// /* -// Recording and responding to activity -// */ - -func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { - var expectedCall *Call - - for i, call := range m.ExpectedCalls { - if call.Method == method { - _, diffCount := call.Arguments.Diff(arguments) - if diffCount == 0 { - expectedCall = call - if call.Repeatability > -1 { - return i, call - } - } - } - } - - return -1, expectedCall -} - -func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { - var diffCount int - var closestCall *Call - var err string - - for _, call := range m.expectedCalls() { - if call.Method == method { - - errInfo, tempDiffCount := call.Arguments.Diff(arguments) - if tempDiffCount < diffCount || diffCount == 0 { - diffCount = tempDiffCount - closestCall = call - err = errInfo - } - - } - } - - return closestCall, err -} - -func callString(method string, arguments Arguments, includeArgumentValues bool) string { - - var argValsString string - if includeArgumentValues { - var argVals []string - for argIndex, arg := range arguments { - argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) - } - argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) - } - - return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) -} - -// Called tells the mock object that a method has been called, and gets an array -// of arguments to return. Panics if the call is unexpected (i.e. not preceded by -// appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) Called(arguments ...interface{}) Arguments { - // get the calling function's name - pc, _, _, ok := runtime.Caller(1) - if !ok { - panic("Couldn't get the caller information") - } - functionPath := runtime.FuncForPC(pc).Name() - //Next four lines are required to use GCCGO function naming conventions. - //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock - //uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree - //With GCCGO we need to remove interface information starting from pN
. - re := regexp.MustCompile("\\.pN\\d+_") - if re.MatchString(functionPath) { - functionPath = re.Split(functionPath, -1)[0] - } - parts := strings.Split(functionPath, ".") - functionName := parts[len(parts)-1] - return m.MethodCalled(functionName, arguments...) -} - -// MethodCalled tells the mock object that the given method has been called, and gets -// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded -// by appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { - m.mutex.Lock() - //TODO: could combine expected and closes in single loop - found, call := m.findExpectedCall(methodName, arguments...) - - if found < 0 { - // expected call found but it has already been called with repeatable times - if call != nil { - m.mutex.Unlock() - m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - // we have to fail here - because we don't know what to do - // as the return arguments. This is because: - // - // a) this is a totally unexpected call to this method, - // b) the arguments are not what was expected, or - // c) the developer has forgotten to add an accompanying On...Return pair. - closestCall, mismatch := m.findClosestCall(methodName, arguments...) - m.mutex.Unlock() - - if closestCall != nil { - m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", - callString(methodName, arguments, true), - callString(methodName, closestCall.Arguments, true), - diffArguments(closestCall.Arguments, arguments), - strings.TrimSpace(mismatch), - ) - } else { - m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - } - - if call.Repeatability == 1 { - call.Repeatability = -1 - } else if call.Repeatability > 1 { - call.Repeatability-- - } - call.totalCalls++ - - // add the call - m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) - m.mutex.Unlock() - - // block if specified - if call.WaitFor != nil { - <-call.WaitFor - } else { - time.Sleep(call.waitTime) - } - - m.mutex.Lock() - runFn := call.RunFn - m.mutex.Unlock() - - if runFn != nil { - runFn(arguments) - } - - m.mutex.Lock() - returnArgs := call.ReturnArguments - m.mutex.Unlock() - - return returnArgs -} - -/* - Assertions -*/ - -type assertExpectationser interface { - AssertExpectations(TestingT) bool -} - -// AssertExpectationsForObjects asserts that everything specified with On and Return -// of the specified objects was in fact called as expected. -// -// Calls may have occurred in any order. -func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - for _, obj := range testObjects { - if m, ok := obj.(Mock); ok { - t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = &m - } - m := obj.(assertExpectationser) - if !m.AssertExpectations(t) { - t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) - return false - } - } - return true -} - -// AssertExpectations asserts that everything specified with On and Return was -// in fact called as expected. Calls may have occurred in any order. -func (m *Mock) AssertExpectations(t TestingT) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - var somethingMissing bool - var failedExpectations int - - // iterate through each expectation - expectedCalls := m.expectedCalls() - for _, expectedCall := range expectedCalls { - if !expectedCall.optional && !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 { - somethingMissing = true - failedExpectations++ - t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) - } else { - if expectedCall.Repeatability > 0 { - somethingMissing = true - failedExpectations++ - t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) - } else { - t.Logf("PASS:\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String()) - } - } - } - - if somethingMissing { - t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) - } - - return !somethingMissing -} - -// AssertNumberOfCalls asserts that the method was called expectedCalls times. -func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - var actualCalls int - for _, call := range m.calls() { - if call.Method == methodName { - actualCalls++ - } - } - return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) -} - -// AssertCalled asserts that the method was called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if !m.methodWasCalled(methodName, arguments) { - var calledWithArgs []string - for _, call := range m.calls() { - calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) - } - if len(calledWithArgs) == 0 { - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) - } - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) - } - return true -} - -// AssertNotCalled asserts that the method was not called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if m.methodWasCalled(methodName, arguments) { - return assert.Fail(t, "Should not have called with given arguments", - fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) - } - return true -} - -func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { - for _, call := range m.calls() { - if call.Method == methodName { - - _, differences := Arguments(expected).Diff(call.Arguments) - - if differences == 0 { - // found the expected call - return true - } - - } - } - // we didn't find the expected call - return false -} - -func (m *Mock) expectedCalls() []*Call { - return append([]*Call{}, m.ExpectedCalls...) -} - -func (m *Mock) calls() []Call { - return append([]Call{}, m.Calls...) -} - -/* - Arguments -*/ - -// Arguments holds an array of method arguments or return values. -type Arguments []interface{} - -const ( - // Anything is used in Diff and Assert when the argument being tested - // shouldn't be taken into consideration. - Anything = "mock.Anything" -) - -// AnythingOfTypeArgument is a string that contains the type of an argument -// for use when type checking. Used in Diff and Assert. -type AnythingOfTypeArgument string - -// AnythingOfType returns an AnythingOfTypeArgument object containing the -// name of the type to check for. Used in Diff and Assert. -// -// For example: -// Assert(t, AnythingOfType("string"), AnythingOfType("int")) -func AnythingOfType(t string) AnythingOfTypeArgument { - return AnythingOfTypeArgument(t) -} - -// IsTypeArgument is a struct that contains the type of an argument -// for use when type checking. This is an alternative to AnythingOfType. -// Used in Diff and Assert. -type IsTypeArgument struct { - t interface{} -} - -// IsType returns an IsTypeArgument object containing the type to check for. -// You can provide a zero-value of the type to check. This is an -// alternative to AnythingOfType. Used in Diff and Assert. -// -// For example: -// Assert(t, IsType(""), IsType(0)) -func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: t} -} - -// argumentMatcher performs custom argument matching, returning whether or -// not the argument is matched by the expectation fixture function. -type argumentMatcher struct { - // fn is a function which accepts one argument, and returns a bool. - fn reflect.Value -} - -func (f argumentMatcher) Matches(argument interface{}) bool { - expectType := f.fn.Type().In(0) - expectTypeNilSupported := false - switch expectType.Kind() { - case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: - expectTypeNilSupported = true - } - - argType := reflect.TypeOf(argument) - var arg reflect.Value - if argType == nil { - arg = reflect.New(expectType).Elem() - } else { - arg = reflect.ValueOf(argument) - } - - if argType == nil && !expectTypeNilSupported { - panic(errors.New("attempting to call matcher with nil for non-nil expected type")) - } - if argType == nil || argType.AssignableTo(expectType) { - result := f.fn.Call([]reflect.Value{arg}) - return result[0].Bool() - } - return false -} - -func (f argumentMatcher) String() string { - return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name()) -} - -// MatchedBy can be used to match a mock call based on only certain properties -// from a complex struct or some calculation. It takes a function that will be -// evaluated with the called argument and will return true when there's a match -// and false otherwise. -// -// Example: -// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) -// -// |fn|, must be a function accepting a single argument (of the expected type) -// which returns a bool. If |fn| doesn't match the required signature, -// MatchedBy() panics. -func MatchedBy(fn interface{}) argumentMatcher { - fnType := reflect.TypeOf(fn) - - if fnType.Kind() != reflect.Func { - panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) - } - if fnType.NumIn() != 1 { - panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) - } - if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { - panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) - } - - return argumentMatcher{fn: reflect.ValueOf(fn)} -} - -// Get Returns the argument at the specified index. -func (args Arguments) Get(index int) interface{} { - if index+1 > len(args) { - panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) - } - return args[index] -} - -// Is gets whether the objects match the arguments specified. -func (args Arguments) Is(objects ...interface{}) bool { - for i, obj := range args { - if obj != objects[i] { - return false - } - } - return true -} - -// Diff gets a string describing the differences between the arguments -// and the specified objects. -// -// Returns the diff string and number of differences found. -func (args Arguments) Diff(objects []interface{}) (string, int) { - //TODO: could return string as error and nil for No difference - - var output = "\n" - var differences int - - var maxArgCount = len(args) - if len(objects) > maxArgCount { - maxArgCount = len(objects) - } - - for i := 0; i < maxArgCount; i++ { - var actual, expected interface{} - var actualFmt, expectedFmt string - - if len(objects) <= i { - actual = "(Missing)" - actualFmt = "(Missing)" - } else { - actual = objects[i] - actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) - } - - if len(args) <= i { - expected = "(Missing)" - expectedFmt = "(Missing)" - } else { - expected = args[i] - expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) - } - - if matcher, ok := expected.(argumentMatcher); ok { - if matcher.Matches(actual) { - output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) - } else { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) - } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { - - // type checking - if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) - } - - } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { - t := expected.(*IsTypeArgument).t - if reflect.TypeOf(t) != reflect.TypeOf(actual) { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) - } - } else { - - // normal checking - - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } - } - - } - - if differences == 0 { - return "No differences.", differences - } - - return output, differences - -} - -// Assert compares the arguments with the specified objects and fails if -// they do not exactly match. -func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - // get the differences - diff, diffCount := args.Diff(objects) - - if diffCount == 0 { - return true - } - - // there are differences... report them... - t.Logf(diff) - t.Errorf("%sArguments do not match.", assert.CallerInfo()) - - return false - -} - -// String gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -// -// If no index is provided, String() returns a complete string representation -// of the arguments. -func (args Arguments) String(indexOrNil ...int) string { - - if len(indexOrNil) == 0 { - // normal String() method - return a string representation of the args - var argsStr []string - for _, arg := range args { - argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg))) - } - return strings.Join(argsStr, ",") - } else if len(indexOrNil) == 1 { - // Index has been specified - get the argument at that index - var index = indexOrNil[0] - var s string - var ok bool - if s, ok = args.Get(index).(string); !ok { - panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) - } - return s - } - - panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) - -} - -// Int gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Int(index int) int { - var s int - var ok bool - if s, ok = args.Get(index).(int); !ok { - panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -// Error gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Error(index int) error { - obj := args.Get(index) - var s error - var ok bool - if obj == nil { - return nil - } - if s, ok = obj.(error); !ok { - panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -// Bool gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Bool(index int) bool { - var s bool - var ok bool - if s, ok = args.Get(index).(bool); !ok { - panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { - t := reflect.TypeOf(v) - k := t.Kind() - - if k == reflect.Ptr { - t = t.Elem() - k = t.Kind() - } - return t, k -} - -func diffArguments(expected Arguments, actual Arguments) string { - if len(expected) != len(actual) { - return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) - } - - for x := range expected { - if diffString := diff(expected[x], actual[x]); diffString != "" { - return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) - } - } - - return "" -} - -// diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice or array. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { - if expected == nil || actual == nil { - return "" - } - - et, ek := typeAndKind(expected) - at, _ := typeAndKind(actual) - - if et != at { - return "" - } - - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { - return "" - } - - e := spewConfig.Sdump(expected) - a := spewConfig.Sdump(actual) - - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return diff -} - -var spewConfig = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, -} - -type tHelper interface { - Helper() -} diff --git a/vendor/golang.org/x/sync/AUTHORS b/vendor/golang.org/x/sync/AUTHORS deleted file mode 100644 index 15167cd74..000000000 --- a/vendor/golang.org/x/sync/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/sync/CONTRIBUTORS b/vendor/golang.org/x/sync/CONTRIBUTORS deleted file mode 100644 index 1c4577e96..000000000 --- a/vendor/golang.org/x/sync/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/vendor/golang.org/x/sync/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS deleted file mode 100644 index 733099041..000000000 --- a/vendor/golang.org/x/sync/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/syncmap/go19.go b/vendor/golang.org/x/sync/syncmap/go19.go deleted file mode 100644 index 41a59091e..000000000 --- a/vendor/golang.org/x/sync/syncmap/go19.go +++ /dev/null @@ -1,17 +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. - -// +build go1.9 - -package syncmap - -import "sync" // home to the standard library's sync.map implementation as of Go 1.9 - -// Map is a concurrent map with amortized-constant-time loads, stores, and deletes. -// It is safe for multiple goroutines to call a Map's methods concurrently. -// -// The zero Map is valid and empty. -// -// A Map must not be copied after first use. -type Map = sync.Map diff --git a/vendor/golang.org/x/sync/syncmap/map.go b/vendor/golang.org/x/sync/syncmap/map.go deleted file mode 100644 index 4b638cb7a..000000000 --- a/vendor/golang.org/x/sync/syncmap/map.go +++ /dev/null @@ -1,8 +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 syncmap provides a concurrent map implementation. -// This was the prototype for sync.Map which was added to the standard library's -// sync package in Go 1.9. https://golang.org/pkg/sync/#Map. -package syncmap diff --git a/vendor/golang.org/x/sync/syncmap/pre_go19.go b/vendor/golang.org/x/sync/syncmap/pre_go19.go deleted file mode 100644 index 01a7be7f9..000000000 --- a/vendor/golang.org/x/sync/syncmap/pre_go19.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2016 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.9 - -package syncmap - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -// Map is a concurrent map with amortized-constant-time loads, stores, and deletes. -// It is safe for multiple goroutines to call a Map's methods concurrently. -// -// The zero Map is valid and empty. -// -// A Map must not be copied after first use. -type Map struct { - mu sync.Mutex - - // read contains the portion of the map's contents that are safe for - // concurrent access (with or without mu held). - // - // The read field itself is always safe to load, but must only be stored with - // mu held. - // - // Entries stored in read may be updated concurrently without mu, but updating - // a previously-expunged entry requires that the entry be copied to the dirty - // map and unexpunged with mu held. - read atomic.Value // readOnly - - // dirty contains the portion of the map's contents that require mu to be - // held. To ensure that the dirty map can be promoted to the read map quickly, - // it also includes all of the non-expunged entries in the read map. - // - // Expunged entries are not stored in the dirty map. An expunged entry in the - // clean map must be unexpunged and added to the dirty map before a new value - // can be stored to it. - // - // If the dirty map is nil, the next write to the map will initialize it by - // making a shallow copy of the clean map, omitting stale entries. - dirty map[interface{}]*entry - - // misses counts the number of loads since the read map was last updated that - // needed to lock mu to determine whether the key was present. - // - // Once enough misses have occurred to cover the cost of copying the dirty - // map, the dirty map will be promoted to the read map (in the unamended - // state) and the next store to the map will make a new dirty copy. - misses int -} - -// readOnly is an immutable struct stored atomically in the Map.read field. -type readOnly struct { - m map[interface{}]*entry - amended bool // true if the dirty map contains some key not in m. -} - -// expunged is an arbitrary pointer that marks entries which have been deleted -// from the dirty map. -var expunged = unsafe.Pointer(new(interface{})) - -// An entry is a slot in the map corresponding to a particular key. -type entry struct { - // p points to the interface{} value stored for the entry. - // - // If p == nil, the entry has been deleted and m.dirty == nil. - // - // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry - // is missing from m.dirty. - // - // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty - // != nil, in m.dirty[key]. - // - // An entry can be deleted by atomic replacement with nil: when m.dirty is - // next created, it will atomically replace nil with expunged and leave - // m.dirty[key] unset. - // - // An entry's associated value can be updated by atomic replacement, provided - // p != expunged. If p == expunged, an entry's associated value can be updated - // only after first setting m.dirty[key] = e so that lookups using the dirty - // map find the entry. - p unsafe.Pointer // *interface{} -} - -func newEntry(i interface{}) *entry { - return &entry{p: unsafe.Pointer(&i)} -} - -// Load returns the value stored in the map for a key, or nil if no -// value is present. -// The ok result indicates whether value was found in the map. -func (m *Map) Load(key interface{}) (value interface{}, ok bool) { - read, _ := m.read.Load().(readOnly) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - // Avoid reporting a spurious miss if m.dirty got promoted while we were - // blocked on m.mu. (If further loads of the same key will not miss, it's - // not worth copying the dirty map for this key.) - read, _ = m.read.Load().(readOnly) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - if !ok { - return nil, false - } - return e.load() -} - -func (e *entry) load() (value interface{}, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { - return nil, false - } - return *(*interface{})(p), true -} - -// Store sets the value for a key. -func (m *Map) Store(key, value interface{}) { - read, _ := m.read.Load().(readOnly) - if e, ok := read.m[key]; ok && e.tryStore(&value) { - return - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnly) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - // The entry was previously expunged, which implies that there is a - // non-nil dirty map and this entry is not in it. - m.dirty[key] = e - } - e.storeLocked(&value) - } else if e, ok := m.dirty[key]; ok { - e.storeLocked(&value) - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(readOnly{m: read.m, amended: true}) - } - m.dirty[key] = newEntry(value) - } - m.mu.Unlock() -} - -// tryStore stores a value if the entry has not been expunged. -// -// If the entry is expunged, tryStore returns false and leaves the entry -// unchanged. -func (e *entry) tryStore(i *interface{}) bool { - p := atomic.LoadPointer(&e.p) - if p == expunged { - return false - } - for { - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { - return true - } - p = atomic.LoadPointer(&e.p) - if p == expunged { - return false - } - } -} - -// unexpungeLocked ensures that the entry is not marked as expunged. -// -// If the entry was previously expunged, it must be added to the dirty map -// before m.mu is unlocked. -func (e *entry) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expunged, nil) -} - -// storeLocked unconditionally stores a value to the entry. -// -// The entry must be known not to be expunged. -func (e *entry) storeLocked(i *interface{}) { - atomic.StorePointer(&e.p, unsafe.Pointer(i)) -} - -// LoadOrStore returns the existing value for the key if present. -// Otherwise, it stores and returns the given value. -// The loaded result is true if the value was loaded, false if stored. -func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { - // Avoid locking if it's a clean hit. - read, _ := m.read.Load().(readOnly) - if e, ok := read.m[key]; ok { - actual, loaded, ok := e.tryLoadOrStore(value) - if ok { - return actual, loaded - } - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnly) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - actual, loaded, _ = e.tryLoadOrStore(value) - } else if e, ok := m.dirty[key]; ok { - actual, loaded, _ = e.tryLoadOrStore(value) - m.missLocked() - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(readOnly{m: read.m, amended: true}) - } - m.dirty[key] = newEntry(value) - actual, loaded = value, false - } - m.mu.Unlock() - - return actual, loaded -} - -// tryLoadOrStore atomically loads or stores a value if the entry is not -// expunged. -// -// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and -// returns with ok==false. -func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == expunged { - return nil, false, false - } - if p != nil { - return *(*interface{})(p), true, true - } - - // Copy the interface after the first load to make this method more amenable - // to escape analysis: if we hit the "load" path or the entry is expunged, we - // shouldn't bother heap-allocating. - ic := i - for { - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { - return i, false, true - } - p = atomic.LoadPointer(&e.p) - if p == expunged { - return nil, false, false - } - if p != nil { - return *(*interface{})(p), true, true - } - } -} - -// Delete deletes the value for a key. -func (m *Map) Delete(key interface{}) { - read, _ := m.read.Load().(readOnly) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read, _ = m.read.Load().(readOnly) - e, ok = read.m[key] - if !ok && read.amended { - delete(m.dirty, key) - } - m.mu.Unlock() - } - if ok { - e.delete() - } -} - -func (e *entry) delete() (hadValue bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { - return false - } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return true - } - } -} - -// Range calls f sequentially for each key and value present in the map. -// If f returns false, range stops the iteration. -// -// Range does not necessarily correspond to any consistent snapshot of the Map's -// contents: no key will be visited more than once, but if the value for any key -// is stored or deleted concurrently, Range may reflect any mapping for that key -// from any point during the Range call. -// -// Range may be O(N) with the number of elements in the map even if f returns -// false after a constant number of calls. -func (m *Map) Range(f func(key, value interface{}) bool) { - // We need to be able to iterate over all of the keys that were already - // present at the start of the call to Range. - // If read.amended is false, then read.m satisfies that property without - // requiring us to hold m.mu for a long time. - read, _ := m.read.Load().(readOnly) - if read.amended { - // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) - // (assuming the caller does not break out early), so a call to Range - // amortizes an entire copy of the map: we can promote the dirty copy - // immediately! - m.mu.Lock() - read, _ = m.read.Load().(readOnly) - if read.amended { - read = readOnly{m: m.dirty} - m.read.Store(read) - m.dirty = nil - m.misses = 0 - } - m.mu.Unlock() - } - - for k, e := range read.m { - v, ok := e.load() - if !ok { - continue - } - if !f(k, v) { - break - } - } -} - -func (m *Map) missLocked() { - m.misses++ - if m.misses < len(m.dirty) { - return - } - m.read.Store(readOnly{m: m.dirty}) - m.dirty = nil - m.misses = 0 -} - -func (m *Map) dirtyLocked() { - if m.dirty != nil { - return - } - - read, _ := m.read.Load().(readOnly) - m.dirty = make(map[interface{}]*entry, len(read.m)) - for k, e := range read.m { - if !e.tryExpungeLocked() { - m.dirty[k] = e - } - } -} - -func (e *entry) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) - for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { - return true - } - p = atomic.LoadPointer(&e.p) - } - return p == expunged -} diff --git a/vendor/modules.txt b/vendor/modules.txt index cd553c17b..4ded9f5ce 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -302,8 +302,6 @@ github.com/mat/besticon/ico github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.10 github.com/mattn/go-isatty -# github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f -github.com/mattn/go-pointer # github.com/mattn/go-runewidth v0.0.6 github.com/mattn/go-runewidth # github.com/matttproud/golang_protobuf_extensions v1.0.1 @@ -408,11 +406,8 @@ github.com/status-im/tcp-shaker github.com/steakknife/bloomfilter # github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 github.com/steakknife/hamming -# github.com/stretchr/objx v0.1.1 -github.com/stretchr/objx # github.com/stretchr/testify v1.5.1 github.com/stretchr/testify/assert -github.com/stretchr/testify/mock github.com/stretchr/testify/require github.com/stretchr/testify/suite # github.com/syndtr/goleveldb v1.0.0 @@ -528,8 +523,6 @@ golang.org/x/net/internal/iana golang.org/x/net/internal/socket golang.org/x/net/ipv4 golang.org/x/net/publicsuffix -# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e -golang.org/x/sync/syncmap # golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 golang.org/x/sys/cpu golang.org/x/sys/unix