diff --git a/test/go/test-wallet_connect/combined/index.html b/test/go/test-wallet_connect/combined/index.html deleted file mode 100644 index 5a90510263..0000000000 --- a/test/go/test-wallet_connect/combined/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - App and Integration - - - -

App Page

- -

Wallet Page

- - - diff --git a/test/go/test-wallet_connect/modal/modal.go b/test/go/test-wallet_connect/modal/modal.go index 47c17ef40d..db32e2a76c 100644 --- a/test/go/test-wallet_connect/modal/modal.go +++ b/test/go/test-wallet_connect/modal/modal.go @@ -11,6 +11,9 @@ import ( webview "github.com/webview/webview_go" ) +// main simulates a wallet connect client session with status registered as a wallet connect provider +// this is used as an alternative for testing the deep link integration until the status desktop app goes in production +// and we can register it as a wallet connect provider func main() { // Serve files from the ./generated directory fileServer := http.FileServer(http.Dir("./generated")) diff --git a/test/go/test-wallet_connect/sdk/helpers.go b/test/go/test-wallet_connect/sdk/helpers.go deleted file mode 100644 index d6e5a9be49..0000000000 --- a/test/go/test-wallet_connect/sdk/helpers.go +++ /dev/null @@ -1,207 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "reflect" - - statusgo "github.com/status-im/status-go/mobile" - "github.com/status-im/status-go/multiaccounts" -) - -func loginToAccount(hashedPassword, userFolder, nodeConfigJson string) error { - absUserFolder, err := filepath.Abs(userFolder) - if err != nil { - return err - } - accountsJson := statusgo.OpenAccounts(absUserFolder) - accounts := make([]multiaccounts.Account, 0) - err = getCAPIResponse(accountsJson, &accounts) - if err != nil { - return err - } - - if len(accounts) == 0 { - return fmt.Errorf("no accounts found") - } - - account := accounts[0] - keystorePath := filepath.Join(filepath.Join(absUserFolder, "keystore/"), account.KeyUID) - initKeystoreJson := statusgo.InitKeystore(keystorePath) - apiResponse := statusgo.APIResponse{} - err = getCAPIResponse(initKeystoreJson, &apiResponse) - if err != nil { - return err - } - - //serialize account of type multiaccounts.Account - accountJson, err := json.Marshal(account) - if err != nil { - return err - } - loginJson := statusgo.LoginWithConfig(string(accountJson), hashedPassword, nodeConfigJson) - err = getCAPIResponse(loginJson, &apiResponse) - if err != nil { - return err - } - - return nil -} - -type jsonrpcMessage struct { - Version string `json:"jsonrpc"` - ID json.RawMessage `json:"id"` -} - -type jsonrpcRequest struct { - jsonrpcMessage - ChainID uint64 `json:"chainId"` - Method string `json:"method"` - Params json.RawMessage `json:"params,omitempty"` -} - -func callPrivateMethod(method string, params []interface{}) string { - var paramsJson json.RawMessage - var err error - if params != nil { - paramsJson, err = json.Marshal(params) - if err != nil { - return "" - } - } - - msg := jsonrpcRequest{ - jsonrpcMessage: jsonrpcMessage{ - Version: "2.0", - }, - Method: method, - Params: paramsJson, - } - - msgJson, err := json.Marshal(msg) - if err != nil { - return "" - } - - return statusgo.CallPrivateRPC(string(msgJson)) -} - -type Config struct { - HashedPassword string `json:"hashedPassword"` - NodeConfigFile string `json:"nodeConfigFile"` - DataDir *string `json:"dataDir,omitempty"` -} - -func processConfigArgs() (config *Config, nodeConfigJson string, userFolder string, err error) { - var configFilePath string - flag.StringVar(&configFilePath, "config", "", "path to json config file") - flag.StringVar(&userFolder, "dataDir", "../../../Status/data", "path to json config file") - flag.Parse() - - if configFilePath == "" { - flag.Usage() - return - } - - config = &Config{} - // parse config file - configFile, err := os.Open(configFilePath) - if err != nil { - panic(err) - } - defer configFile.Close() - jsonParser := json.NewDecoder(configFile) - if err = jsonParser.Decode(&config); err != nil { - panic(err) - } - - // Read config.NodeConfigFile json file and store it as string - nodeConfigFile, err := os.Open(config.NodeConfigFile) - if err != nil { - panic(err) - } - defer nodeConfigFile.Close() - nodeConfigData, err := io.ReadAll(nodeConfigFile) - if err == nil { - nodeConfigJson = string(nodeConfigData) - } - - if config.DataDir != nil { - userFolder = *config.DataDir - } - - return -} - -func getCAPIResponse[T any](responseJson string, res T) error { - apiResponse := statusgo.APIResponse{} - err := json.Unmarshal([]byte(responseJson), &apiResponse) - if err == nil { - if apiResponse.Error != "" { - return fmt.Errorf("API error: %s", apiResponse.Error) - } - } - - typeOfT := reflect.TypeOf(res) - kindOfT := typeOfT.Kind() - - // Check for valid types: pointer, slice, map - if kindOfT != reflect.Ptr && kindOfT != reflect.Slice && kindOfT != reflect.Map { - return fmt.Errorf("type T must be a pointer, slice, or map") - } - - if err := json.Unmarshal([]byte(responseJson), &res); err != nil { - return fmt.Errorf("failed to unmarshal data: %w", err) - } - - return nil -} - -type jsonrpcSuccessfulResponse struct { - jsonrpcMessage - Result json.RawMessage `json:"result"` -} - -type jsonrpcErrorResponse struct { - jsonrpcMessage - Error jsonError `json:"error"` -} - -// jsonError represents Error message for JSON-RPC responses. -type jsonError struct { - Code int `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data,omitempty"` -} - -func getRPCAPIResponse[T any](responseJson string, res T) error { - errApiResponse := jsonrpcErrorResponse{} - err := json.Unmarshal([]byte(responseJson), &errApiResponse) - if err == nil && errApiResponse.Error.Code != 0 { - return fmt.Errorf("API error: %#v", errApiResponse.Error) - } - - apiResponse := jsonrpcSuccessfulResponse{} - err = json.Unmarshal([]byte(responseJson), &apiResponse) - if err != nil { - return fmt.Errorf("failed to unmarshal jsonrpcSuccessfulResponse: %w", err) - } - - typeOfT := reflect.TypeOf(res) - kindOfT := typeOfT.Kind() - - // Check for valid types: pointer, slice, map - if kindOfT != reflect.Ptr && kindOfT != reflect.Slice && kindOfT != reflect.Map { - return fmt.Errorf("type T must be a pointer, slice, or map") - } - - if err := json.Unmarshal(apiResponse.Result, &res); err != nil { - return fmt.Errorf("failed to unmarshal data: %w", err) - } - - return nil -} diff --git a/test/go/test-wallet_connect/sdk/index.html b/test/go/test-wallet_connect/sdk/index.html deleted file mode 100644 index 83b96b0188..0000000000 --- a/test/go/test-wallet_connect/sdk/index.html +++ /dev/null @@ -1,383 +0,0 @@ - - - - - Wallet Connect status-go test - - - -
- - - - - diff --git a/test/go/test-wallet_connect/sdk/main.go b/test/go/test-wallet_connect/sdk/main.go deleted file mode 100644 index 892b4ebe48..0000000000 --- a/test/go/test-wallet_connect/sdk/main.go +++ /dev/null @@ -1,229 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "net/http" - "os" - - "github.com/ethereum/go-ethereum/log" - webview "github.com/webview/webview_go" - - statusgo "github.com/status-im/status-go/mobile" - wc "github.com/status-im/status-go/services/wallet/walletconnect" - "github.com/status-im/status-go/services/wallet/walletevent" - "github.com/status-im/status-go/signal" -) - -// l is used for local logging -var l log.Logger - -func init() { - l = log.New() - l.SetHandler(log.CallerFileHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))) -} - -type PairResult struct { - SessionProposal string `json:"sessionProposal"` -} - -type Configuration struct { - ProjectId string `json:"projectId"` -} - -type GoEvent struct { - Name string `json:"name"` - Payload interface{} `json:"payload"` -} - -var eventQueue chan GoEvent = make(chan GoEvent, 10000) - -func signalHandler(jsonEvent string) { - // parse signal.Envelope from jsonEvent - envelope := signal.Envelope{} - err := json.Unmarshal([]byte(jsonEvent), &envelope) - if err != nil { - // check for error in json - apiResponse := statusgo.APIResponse{} - err = json.Unmarshal([]byte(jsonEvent), &apiResponse) - if err != nil { - log.Error("Error parsing the signal event: ", err) - return - } - } - - if envelope.Type == signal.EventNodeReady { - eventQueue <- GoEvent{Name: "nodeReady", Payload: ""} - } else if envelope.Type == "wallet" { - // parse envelope.Event to json - walletEvent := walletevent.Event{} - err := json.Unmarshal([]byte(jsonEvent), &walletEvent) - if err != nil { - log.Error("Error parsing the wallet event: ", err) - return - } - // TODO: continue from here - // if walletEvent.Type == "WalletConnectProposeUserPair" { - // eventQueue <- GoEvent{Name: "proposeUserPair", Payload: walletEvent.Message} - // } - } -} - -func main() { - // Setup status-go logger - log.Root().SetHandler(log.CallerFileHandler(log.StdoutHandler)) - - signal.SetDefaultNodeNotificationHandler(signalHandler) - config, nodeConfigJson, userFolder, err := processConfigArgs() - if err != nil { - panic(err) - } - - // Login to first account - err = loginToAccount(config.HashedPassword, userFolder, nodeConfigJson) - if err != nil { - panic(err) - } - - // Start WebView - w := webview.New(true) - defer w.Destroy() - w.SetTitle("WC status-go test") - w.SetSize(1280, 1024, webview.HintNone) - - w.Bind("sessionRequest", func(sessionRequestJson, hashedPassword string) bool { - fmt.Println("sessionRequestJson:", sessionRequestJson) - sessionReqRes := callPrivateMethod("wallet_wCSessionRequest", []interface{}{sessionRequestJson}) - fmt.Println("sessionReqRes:", sessionReqRes) - var apiResponse wc.SessionRequestResponse - err = getRPCAPIResponse(sessionReqRes, &apiResponse) - if err != nil { - l.Error("Error parsing wallet_wCSessionRequest response", "error", err) - return false - } - if apiResponse.SignOnKeycard { - l.Error("SignOnKeycard is not supported in this test") - return false - } - - sessionReqRes = callPrivateMethod("wallet_wCSignMessage", []interface{}{apiResponse.MessageToSign, apiResponse.Address, hashedPassword}) - fmt.Println("sessionReqRes:", sessionReqRes) - var signature string - err = getRPCAPIResponse(sessionReqRes, &signature) - if err != nil { - l.Error("Error parsing wallet_wCSignMessage response", "error", err) - return false - } - - // TODO: process the request type ... - - go func() { - eventQueue <- GoEvent{Name: "sessionRequestResult", Payload: apiResponse} - }() - - return true - }) - - w.Bind("getConfiguration", func() Configuration { - projectID := os.Getenv("STATUS_BUILD_WALLET_CONNECT_PROJECT_ID") - if projectID == "" { - projectID = "87815d72a81d739d2a7ce15c2cfdefb3" - } - return Configuration{ProjectId: projectID} - }) - - w.Bind("echo", func(message string) bool { - fmt.Println(" WebView:", message) - return true - }) - - // Setup go to webview event queue - w.Bind("popNextEvent", func() GoEvent { - select { - case event := <-eventQueue: - return event - default: - return GoEvent{Name: "", Payload: ""} - } - }) - - mockStatusObject(w) - mockController(w) - - // Start a local server to serve the files - http.HandleFunc("/bundle.js", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0") - http.ServeFile(w, r, "../../../../ui/app/AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js") - }) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0") - http.ServeFile(w, r, "./index.html") - }) - - go http.ListenAndServe(":8081", nil) - - w.Navigate("http://localhost:8081") - w.Run() -} - -func mockController(w webview.WebView) { - w.Bind("controller_recordSuccessfulPairing", func(sessionProposalJson string) { - fmt.Println("controller_recordSuccessfulPairing:", sessionProposalJson) - sessionProposalRes := callPrivateMethod("wallet_wCRecordSuccessfulPairing", []interface{}{sessionProposalJson}) - var apiResponse wc.PairSessionResponse - err := getRPCAPIResponse(sessionProposalRes, &apiResponse) - if err != nil { - l.Error("Error parsing the API response", "error", err) - } - }) - - w.Bind("controller_changePairingState", func(topic string, active bool) { - sessionProposalRes := callPrivateMethod("wallet_wCChangePairingState", []interface{}{topic, active}) - var apiResponse wc.PairSessionResponse - err := getRPCAPIResponse(sessionProposalRes, &apiResponse) - if err != nil { - l.Error("Error parsing the API response", "error", err) - } - }) -} - -func mockStatusObject(w webview.WebView) { - w.Bind("statusObject_sdkInitialized", func(error string) { - // All OK here - }) - w.Bind("statusObject_onSessionProposal", func(sessionProposalJson string) bool { - sessionProposalRes := callPrivateMethod("wallet_wCPairSessionProposal", []interface{}{sessionProposalJson}) - var apiResponse wc.PairSessionResponse - err := getRPCAPIResponse(sessionProposalRes, &apiResponse) - if err != nil { - l.Error("Error parsing the API response", "error", err) - return false - } - - go func() { - eventQueue <- GoEvent{Name: "proposeUserPair", Payload: apiResponse} - }() - - return true - }) - w.Bind("statusObject_onSessionRequest", func(sessionRequestJson string) bool { - sessionReqRes := callPrivateMethod("wallet_wCSessionRequest", []interface{}{sessionRequestJson}) - var apiResponse wc.SessionRequestResponse - err := getRPCAPIResponse(sessionReqRes, &apiResponse) - if err != nil { - l.Error("Error parsing the API response", "error", err) - return false - } - return true - }) - - // function onSessionUpdate(details) - // function onSessionExtend(details) - // function onSessionPing(details) - // function onSessionDelete(details) - // function onSessionExpire(details) - // function onSessionRequestSent(details) - // function onSessionEvent(details) - // function onProposalExpire(details) -} diff --git a/test/status-go/integration/.gitignore b/test/status-go/integration/.gitignore new file mode 100644 index 0000000000..1300e1343d --- /dev/null +++ b/test/status-go/integration/.gitignore @@ -0,0 +1 @@ +.integration_tests_config.json \ No newline at end of file diff --git a/test/status-go/integration/README.md b/test/status-go/integration/README.md new file mode 100644 index 0000000000..1a8a50a5c9 --- /dev/null +++ b/test/status-go/integration/README.md @@ -0,0 +1,30 @@ +# Development integration tests for status-go + +These integration tests are an experiment. They rely on an existing developer environment (working user folder, blockchain access tokens) and internet connection. + +If it proves its usefulness we might consider automating them and make it independent of internet services + +## How to run tests + +Setup steps + +- Dump the node config passed to `Login` status-go call as `.node_config.json` and use its path later on in as `nodeConfigFile` in `.integration_tests_config.json` + - Ensure the blockchain access tokens are configured when dumping the configuration file +- Copy [integration_tests_config-template.json](./integration_tests_config-template.json) to tests sub-folders and rename it as `.integration_tests_config.json`, then update it with your own values. + - Update `nodeConfigFile` with the previously extracted node config path + - The `hashedPassword` should be the "0x" + `keccak256(clearPassword)` + - For `dataDir` it is expected an working status-go user folder (e.g. the usual `status-desktop/Second/data` used with `make run` command) + +Run wallet tests + +- once + + ```sh + (cd test/status-go/integration && go test -v ./wallet/... --tags=gowaku_no_rln,gowaku_skip_migrations) + ``` + +- continuously on code changes + + ```sh + (cd test/status-go/integration && nodemon --watch ../../../vendor/status-go/ --watch . --ext "*.go,*.sql" --exec 'go test -v ./wallet/... --tags=gowaku_no_rln,gowaku_skip_migrations 2>&1 | tee ~/proj/tmp/status-go-tests.log || exit 1') + ``` diff --git a/test/go/test-wallet_connect/sdk/go.mod b/test/status-go/integration/go.mod similarity index 94% rename from test/go/test-wallet_connect/sdk/go.mod rename to test/status-go/integration/go.mod index e46d35dd22..a7b9445939 100644 --- a/test/go/test-wallet_connect/sdk/go.mod +++ b/test/status-go/integration/go.mod @@ -1,8 +1,10 @@ -module main +module github.com/status-im/status-desktop/test/status-go/integration go 1.20 -replace github.com/status-im/status-go => ../../../../vendor/status-go +replace github.com/status-im/status-desktop/test/status-go/integration => ./ + +replace github.com/status-im/status-go => ../../../vendor/status-go // Keep these in sync with status-go/go.mod aliases replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.11 @@ -18,14 +20,13 @@ replace github.com/mutecomm/go-sqlcipher/v4 v4.4.2 => github.com/status-im/go-sq require ( github.com/ethereum/go-ethereum v1.10.26 github.com/status-im/status-go v0.171.7 - github.com/webview/webview_go v0.0.0-20230901181450-5a14030a9070 + github.com/stretchr/testify v1.8.4 ) require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/PuerkitoBio/goquery v1.6.1 // indirect github.com/RoaringBitmap/roaring v0.9.4 // indirect - github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect github.com/anacrolix/chansync v0.3.0 // indirect @@ -46,6 +47,7 @@ require ( github.com/anacrolix/utp v0.1.0 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/cascadia v1.2.0 // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beevik/ntp v0.3.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -70,7 +72,7 @@ require ( github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect @@ -79,7 +81,7 @@ require ( github.com/forPelevin/gomoji v1.1.2 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect - github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -211,7 +213,7 @@ require ( github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/schollz/peerdiscovery v1.7.0 // indirect - github.com/shirou/gopsutil v3.21.5+incompatible // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/siphiuel/lc-proxy-wrapper v0.0.0-20230516150924-246507cee8c7 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -222,9 +224,9 @@ require ( github.com/status-im/migrate/v4 v4.6.2-status.3 // indirect github.com/status-im/rendezvous v1.3.7 // indirect github.com/status-im/status-go/extkeys v1.1.2 // indirect - github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 // indirect + github.com/status-im/tcp-shaker v1.1.1-status // indirect github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 // indirect - github.com/stretchr/testify v1.8.4 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/tklauser/numcpus v0.2.2 // indirect @@ -234,8 +236,8 @@ require ( github.com/vacp2p/mvds v0.0.24-0.20201124060106-26d8e94130d8 // indirect github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 // indirect github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7 // indirect - github.com/waku-org/go-waku v0.8.1-0.20231103161423-351dd55a1498 // indirect - github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd // indirect + github.com/waku-org/go-waku v0.8.1-0.20240104144340-585648c4eefe // indirect + github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 // indirect github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b // indirect github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 // indirect github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1 // indirect @@ -250,6 +252,7 @@ require ( github.com/yeqown/go-qrcode/v2 v2.2.1 // indirect github.com/yeqown/go-qrcode/writer/standard v1.2.1 // indirect github.com/yeqown/reedsolomon v1.0.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zenthangplus/goccm v0.0.0-20211005163543-2f2e522aca15 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.11.0 // indirect diff --git a/test/go/test-wallet_connect/sdk/go.sum b/test/status-go/integration/go.sum similarity index 99% rename from test/go/test-wallet_connect/sdk/go.sum rename to test/status-go/integration/go.sum index ad57ef40ec..c96385c8e4 100644 --- a/test/go/test-wallet_connect/sdk/go.sum +++ b/test/status-go/integration/go.sum @@ -158,7 +158,6 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY= github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= @@ -346,6 +345,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -711,8 +712,9 @@ github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRP github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -828,8 +830,9 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -1900,8 +1903,9 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc= github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= @@ -1999,8 +2003,8 @@ github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hG github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM= github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU= github.com/status-im/status-go/extkeys v1.1.2/go.mod h1:hCmFzb2jiiVF2voZKYbzuhOQiHHCmyLJsZJXrFFg7BY= -github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 h1:oa0KU5jJRNtXaM/P465MhvSFo/HM2O8qi2DDuPcd7ro= -github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= +github.com/status-im/tcp-shaker v1.1.1-status h1:TnVeeWlq2SKCWotHc4Vi6qZQfY8TTe3VLmu1xpEFYhg= +github.com/status-im/tcp-shaker v1.1.1-status/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 h1:sPkzT7Z7uLmejOsBRlZ0kwDWpqjpHJsp834o5nbhqho= github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857/go.mod h1:lq9I5ROto5tcua65GmCE6SIW7VE0ucdEBs1fn4z7uWU= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= @@ -2013,7 +2017,9 @@ github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -2025,7 +2031,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -2088,10 +2096,10 @@ github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZF github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw= github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7 h1:0e1h+p84yBp0IN7AqgbZlV7lgFBjm214lgSOE7CeJmE= github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7/go.mod h1:pFvOZ9YTFsW0o5zJW7a0B5tr1owAijRWJctXJ2toL04= -github.com/waku-org/go-waku v0.8.1-0.20231103161423-351dd55a1498 h1:2Y06Ni3tBj2LQA0ys1o1PspZxZPM9GOKwNEGolbueQ4= -github.com/waku-org/go-waku v0.8.1-0.20231103161423-351dd55a1498/go.mod h1:hem2hnXK5BdabxwJULszM0Rh1Yj+gD9IxjwLCGPPaxs= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd h1:cu7CsUo7BK6ac/v193RIaqAzUcmpa6MNY4xYW9AenQI= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd/go.mod h1:1PdBdPzyTaKt3VnpAHk3zj+r9dXPFOr3IHZP9nFle6E= +github.com/waku-org/go-waku v0.8.1-0.20240104144340-585648c4eefe h1:2D97fbaKlIQRjWMz/iTjnYcxi2z6ekKvspTGtcuPHgU= +github.com/waku-org/go-waku v0.8.1-0.20240104144340-585648c4eefe/go.mod h1:+b5fPPJ4YUIAPJtPOtwB7bTrOQ9lF15I2LnQjV6NMIA= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 h1:jisj+OCI6QydLtFq3Pyhu49wl9ytPN7oAHjMfepHDrA= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59/go.mod h1:1PdBdPzyTaKt3VnpAHk3zj+r9dXPFOr3IHZP9nFle6E= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b h1:KgZVhsLkxsj5gb/FfndSCQu6VYwALrCOgYI3poR95yE= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 h1:Sd7QD/1Yo2o2M1MY49F8Zr4KNBPUEK5cz5HoXQVJbrs= @@ -2104,8 +2112,6 @@ github.com/wealdtech/go-multicodec v1.4.0 h1:iq5PgxwssxnXGGPTIK1srvt6U5bJwIp7k6k github.com/wealdtech/go-multicodec v1.4.0/go.mod h1:aedGMaTeYkIqi/KCPre1ho5rTb3hGpu/snBOS3GQLw4= github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw= github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU= -github.com/webview/webview_go v0.0.0-20230901181450-5a14030a9070 h1:imZLWyo1ondeQjqfb/eHuYgFiOAYg6ugSMCnGfPTPmg= -github.com/webview/webview_go v0.0.0-20230901181450-5a14030a9070/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -2148,6 +2154,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= diff --git a/test/status-go/integration/helpers/accounts.go b/test/status-go/integration/helpers/accounts.go new file mode 100644 index 0000000000..2cd44cb396 --- /dev/null +++ b/test/status-go/integration/helpers/accounts.go @@ -0,0 +1,47 @@ +package helpers + +import ( + "github.com/status-im/status-go/multiaccounts/accounts" +) + +func GetAllAccounts() (res []accounts.Account, err error) { + jsonRes, err := CallPrivateMethod("accounts_getAccounts", []interface{}{}) + if err != nil { + return nil, err + } + + var allAccounts []accounts.Account + err = GetRPCAPIResponse(jsonRes, &allAccounts) + if err != nil { + return nil, err + } + return allAccounts, nil +} + +func GetWalletWatchOnlyAccounts() (res []accounts.Account, err error) { + accounts, err := GetAllAccounts() + if err != nil { + return nil, err + } + + for _, acc := range accounts { + if !acc.IsWalletNonWatchOnlyAccount() { + res = append(res, acc) + } + } + return res, nil +} + +func GetWalletOperableAccounts() (res []accounts.Account, err error) { + accounts, err := GetAllAccounts() + if err != nil { + return nil, err + } + + for _, acc := range accounts { + if acc.IsWalletAccountReadyForTransaction() { + res = append(res, acc) + } + } + return res, nil +} diff --git a/test/status-go/integration/helpers/helpers.go b/test/status-go/integration/helpers/helpers.go new file mode 100644 index 0000000000..d8c0cfd842 --- /dev/null +++ b/test/status-go/integration/helpers/helpers.go @@ -0,0 +1,314 @@ +package helpers + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/log" + statusgo "github.com/status-im/status-go/mobile" + "github.com/status-im/status-go/multiaccounts" + "github.com/status-im/status-go/services/wallet/walletevent" + "github.com/status-im/status-go/signal" +) + +type StatusGoEventName string + +const NodeReadyEvent StatusGoEventName = "nodeReady" +const WalletEvent StatusGoEventName = "wallet" + +type GoEvent struct { + Name StatusGoEventName `json:"name"` + Payload interface{} `json:"payload"` +} + +type envelope struct { + Type string `json:"type"` + Event json.RawMessage `json:"event"` // Use json.RawMessage to delay parsing +} + +func signalHandler(eventQueue chan GoEvent, jsonEvent string) { + envelope := envelope{} + err := json.Unmarshal([]byte(jsonEvent), &envelope) + if err != nil { + apiResponse := statusgo.APIResponse{} + err = json.Unmarshal([]byte(jsonEvent), &apiResponse) + if err != nil { + log.Error("Error parsing the signal event: ", err) + } else if apiResponse.Error != "" { + log.Error("Error from status-go: ", apiResponse.Error) + } else { + log.Error("Unknown JSON content for event", jsonEvent) + } + return + } + + if envelope.Type == signal.EventNodeReady { + eventQueue <- GoEvent{Name: NodeReadyEvent, Payload: string(envelope.Event)} + } else if envelope.Type == string(WalletEvent) { + walletEvent := walletevent.Event{} + err := json.Unmarshal(envelope.Event, &walletEvent) + if err != nil { + log.Error("Error parsing the wallet event: ", err) + return + } + eventQueue <- GoEvent{Name: WalletEvent, Payload: walletEvent} + } +} + +func LoginToTestAccount(t *testing.T) (eventQueue chan GoEvent, config *Config, l log.Logger) { + l = log.New() + l.SetHandler(log.CallerFileHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))) + + // Setup status-go logger + log.Root().SetHandler(log.CallerFileHandler(log.StdoutHandler)) + + eventQueue = make(chan GoEvent, 10000) + signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + signalHandler(eventQueue, jsonEvent) + }) + + conf, nodeConfigJson, userFolder, err := processConfigArgs("./.integration_tests_config.json") + if err != nil { + t.Fatal(err) + } + config = conf + + // Login to first account + err = loginToAccount(config.HashedPassword, userFolder, nodeConfigJson) + if err != nil { + t.Fatal(err) + } + return +} + +func WaitForEvent(eventQueue chan GoEvent, eventName StatusGoEventName, timeout time.Duration) (event *GoEvent, err error) { + for { + select { + case event := <-eventQueue: + if event.Name == eventName { + return &event, nil + } + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for event %s", eventName) + } + } +} + +func WaitForWalletEvent[T any](eventQueue chan GoEvent, eventName walletevent.EventType, timeout time.Duration) (payload *T, err error) { + var event *GoEvent + for { + event, err = WaitForEvent(eventQueue, WalletEvent, timeout) + if err != nil { + return nil, err + } + + walletEvent, ok := event.Payload.(walletevent.Event) + if !ok { + return nil, errors.New("event payload is not a wallet event") + } + + var newPayload T + if walletEvent.Type == eventName { + if walletEvent.Message != "" { + err = json.Unmarshal([]byte(walletEvent.Message), &newPayload) + if err != nil { + return nil, err + } + return &newPayload, nil + } + return nil, nil + } + } +} + +func loginToAccount(hashedPassword, userFolder, nodeConfigJson string) error { + absUserFolder, err := filepath.Abs(userFolder) + if err != nil { + return err + } + accountsJson := statusgo.OpenAccounts(absUserFolder) + accounts := make([]multiaccounts.Account, 0) + err = GetCAPIResponse(accountsJson, &accounts) + if err != nil { + return err + } + + if len(accounts) == 0 { + return fmt.Errorf("no accounts found") + } + + account := accounts[0] + keystorePath := filepath.Join(filepath.Join(absUserFolder, "keystore/"), account.KeyUID) + initKeystoreJson := statusgo.InitKeystore(keystorePath) + apiResponse := statusgo.APIResponse{} + err = GetCAPIResponse(initKeystoreJson, &apiResponse) + if err != nil { + return err + } + + //serialize account of type multiaccounts.Account + accountJson, err := json.Marshal(account) + if err != nil { + return err + } + loginJson := statusgo.LoginWithConfig(string(accountJson), hashedPassword, nodeConfigJson) + err = GetCAPIResponse(loginJson, &apiResponse) + if err != nil { + return err + } + + return nil +} + +type jsonrpcMessage struct { + Version string `json:"jsonrpc"` + ID json.RawMessage `json:"id"` +} + +type jsonrpcRequest struct { + jsonrpcMessage + ChainID uint64 `json:"chainId"` + Method string `json:"method"` + Params json.RawMessage `json:"params,omitempty"` +} + +func CallPrivateMethod(method string, params []interface{}) (string, error) { + var paramsJson json.RawMessage + var err error + if params != nil { + paramsJson, err = json.Marshal(params) + if err != nil { + return "", err + } + } + + msg := jsonrpcRequest{ + jsonrpcMessage: jsonrpcMessage{ + Version: "2.0", + }, + Method: method, + Params: paramsJson, + } + + msgJson, err := json.Marshal(msg) + if err != nil { + return "", err + } + + return statusgo.CallPrivateRPC(string(msgJson)), nil +} + +type Config struct { + HashedPassword string `json:"hashedPassword"` + NodeConfigFile string `json:"nodeConfigFile"` + DataDir string `json:"dataDir"` +} + +// processConfigArgs expects that configFilePath points to a JSON file that contains a Config struct +// For now this are for developer to manually run them using an existing user folder. +// TODO: ideally we would generate a temporary user folder to be used in the entire suite. +func processConfigArgs(configFilePath string) (config *Config, nodeConfigJson string, userFolder string, err error) { + config = &Config{} + // parse config file + configFile, err := os.Open(configFilePath) + if err != nil { + return nil, "", "", err + } + defer configFile.Close() + + jsonParser := json.NewDecoder(configFile) + if err = jsonParser.Decode(&config); err != nil { + panic(err) + } + + nodeConfigFile, err := os.Open(config.NodeConfigFile) + if err != nil { + panic(err) + } + defer nodeConfigFile.Close() + + nodeConfigData, err := io.ReadAll(nodeConfigFile) + if err == nil { + nodeConfigJson = string(nodeConfigData) + } + + userFolder = config.DataDir + + return +} + +func GetCAPIResponse[T any](responseJson string, res T) error { + apiResponse := statusgo.APIResponse{} + err := json.Unmarshal([]byte(responseJson), &apiResponse) + if err == nil { + if apiResponse.Error != "" { + return fmt.Errorf("API error: %s", apiResponse.Error) + } + } + + typeOfT := reflect.TypeOf(res) + kindOfT := typeOfT.Kind() + + // Check for valid types: pointer, slice, map + if kindOfT != reflect.Ptr && kindOfT != reflect.Slice && kindOfT != reflect.Map { + return fmt.Errorf("type T must be a pointer, slice, or map") + } + + if err := json.Unmarshal([]byte(responseJson), &res); err != nil { + return fmt.Errorf("failed to unmarshal data: %w", err) + } + + return nil +} + +type jsonrpcSuccessfulResponse struct { + jsonrpcMessage + Result json.RawMessage `json:"result"` +} + +type jsonrpcErrorResponse struct { + jsonrpcMessage + Error jsonError `json:"error"` +} + +// jsonError represents Error message for JSON-RPC responses. +type jsonError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +func GetRPCAPIResponse[T any](responseJson string, res T) error { + errApiResponse := jsonrpcErrorResponse{} + err := json.Unmarshal([]byte(responseJson), &errApiResponse) + if err == nil && errApiResponse.Error.Code != 0 { + return fmt.Errorf("API error: %#v", errApiResponse.Error) + } + + apiResponse := jsonrpcSuccessfulResponse{} + err = json.Unmarshal([]byte(responseJson), &apiResponse) + if err != nil { + return fmt.Errorf("failed to unmarshal jsonrpcSuccessfulResponse: %w", err) + } + + typeOfT := reflect.TypeOf(res) + kindOfT := typeOfT.Kind() + + // Check for valid types: pointer, slice, map + if kindOfT != reflect.Ptr && kindOfT != reflect.Slice && kindOfT != reflect.Map { + return fmt.Errorf("type T must be a pointer, slice, or map") + } + + if err := json.Unmarshal(apiResponse.Result, &res); err != nil { + return fmt.Errorf("failed to unmarshal data: %w", err) + } + + return nil +} diff --git a/test/status-go/integration/integration_tests_config-template.json b/test/status-go/integration/integration_tests_config-template.json new file mode 100644 index 0000000000..577bfc1915 --- /dev/null +++ b/test/status-go/integration/integration_tests_config-template.json @@ -0,0 +1,5 @@ +{ + "hashedPassword": "0x", + "nodeConfigFile": "", + "dataDir": "" +} \ No newline at end of file diff --git a/test/status-go/integration/wallet/pendingtransactions_test.go b/test/status-go/integration/wallet/pendingtransactions_test.go new file mode 100644 index 0000000000..ea032a1ed2 --- /dev/null +++ b/test/status-go/integration/wallet/pendingtransactions_test.go @@ -0,0 +1,85 @@ +// These tests are for development only to be run manually +// There is more work needed to automate them not to depend on an existing account and internet connection + +package wallet + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + + "github.com/status-im/status-desktop/test/status-go/integration/helpers" + + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/services/wallet/bridge" + "github.com/status-im/status-go/services/wallet/transfer" + "github.com/status-im/status-go/transactions" +) + +// TestPendingTx_NotificationStatus tests that a pending transaction is created, then updated and finally deleted. +func TestPendingTx_NotificationStatus(t *testing.T) { + eventQueue, conf, _ := helpers.LoginToTestAccount(t) + + _, err := helpers.WaitForEvent(eventQueue, helpers.NodeReadyEvent, 60*time.Second) + require.NoError(t, err) + + opAccounts, err := helpers.GetWalletOperableAccounts() + require.NoError(t, err) + require.Greater(t, len(opAccounts), 0) + sender := opAccounts[0] + + watchAccounts, err := helpers.GetWalletWatchOnlyAccounts() + require.NoError(t, err) + require.Greater(t, len(watchAccounts), 0) + recipient := watchAccounts[0] + + mTCommand := transfer.MultiTransactionCommand{ + FromAddress: common.Address(sender.Address), + ToAddress: common.Address(recipient.Address), + FromAsset: "ETH", + ToAsset: "ETH", + FromAmount: (*hexutil.Big)(new(big.Int).SetUint64(100000)), + Type: transfer.MultiTransactionSend, + } + data := []*bridge.TransactionBridge{ + { + BridgeName: "Transfer", + ChainID: 5, + TransferTx: &transactions.SendTxArgs{ + From: sender.Address, + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).Set(mTCommand.FromAmount.ToInt())), + }, + }, + } + + password := conf.HashedPassword + + // Step 1: send a transaction that will generate a pending entry + sessionReqRes, err := helpers.CallPrivateMethod("wallet_createMultiTransaction", []interface{}{mTCommand, data, password}) + require.NoError(t, err) + + var apiResponse *transfer.MultiTransactionCommandResult + err = helpers.GetRPCAPIResponse(sessionReqRes, &apiResponse) + require.NoError(t, err) + require.Equal(t, 1, len(apiResponse.Hashes)) + + // Step 2: wait for the pending entry to be confirmed + statusPayload, err := helpers.WaitForWalletEvent[transactions.StatusChangedPayload](eventQueue, transactions.EventPendingTransactionStatusChanged, 60*time.Second) + require.NoError(t, err) + require.Equal(t, statusPayload.Status, transactions.Success) + + // Step 3: Trigger downloading of the new transaction ... + _, err = helpers.CallPrivateMethod("wallet_checkRecentHistoryForChainIDs", []interface{}{[]uint64{5}, []types.Address{sender.Address, recipient.Address}}) + require.NoError(t, err) + + // ... and wait for the new transaction download to trigger deletion from pending_transactions + updatePayload, err := helpers.WaitForWalletEvent[transactions.PendingTxUpdatePayload](eventQueue, transactions.EventPendingTransactionUpdate, 60*time.Second) + require.NoError(t, err) + require.True(t, updatePayload.Deleted) +}