chore(wallet): add integration dev tests for pending tx status update
Remove the wallet connect backend dev test code Updates: #13124
This commit is contained in:
parent
7f6636dc94
commit
70767213ff
|
@ -1,20 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>App and Integration</title>
|
||||
<style>
|
||||
iframe {
|
||||
width: 50vh;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>App Page</h2>
|
||||
<iframe src="http://localhost:8080"></iframe>
|
||||
<h2>Wallet Page</h2>
|
||||
<iframe src="http://localhost:8081"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -1,383 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Wallet Connect status-go test</title>
|
||||
<style></style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script src="bundle.js" type="module"></script>
|
||||
<script>
|
||||
// Helper functions
|
||||
//
|
||||
function goEcho(message) {
|
||||
window.echo(message);
|
||||
}
|
||||
|
||||
// If not null add to it
|
||||
var logEntries = null;
|
||||
|
||||
var newSessionButton;
|
||||
var hashedPasswordInput;
|
||||
|
||||
function addHtmlEntry(htmlContent, color = null, entry = null) {
|
||||
const logDiv = document.getElementById("log");
|
||||
if (entry) {
|
||||
entry.remove();
|
||||
}
|
||||
entry = document.createElement("div");
|
||||
logDiv.appendChild(entry);
|
||||
entry.innerHTML = htmlContent;
|
||||
if (color) {
|
||||
entry.style.color = color;
|
||||
}
|
||||
entry.scrollIntoView();
|
||||
if (logEntries) {
|
||||
logEntries.push(entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
function addLogEntry(message, color = "black", entry = null) {
|
||||
return addHtmlEntry(`${message}`, color, entry);
|
||||
}
|
||||
|
||||
function logComponentStatusChange(componentName, statusMessage, color = "black", entry = null) {
|
||||
const componentHtml = `<span style="color: fuchsia;">${componentName}</span>: `;
|
||||
const statusHtml = `<span style="color: ${color};">${statusMessage}</span>`;
|
||||
return addHtmlEntry(`${componentHtml}${statusHtml}`, null, entry);
|
||||
}
|
||||
|
||||
// SDK initialization
|
||||
//
|
||||
const statusGoEntry = logComponentStatusChange("status-go", "Initializing...");
|
||||
var initEventCount = 0;
|
||||
var eventCount = 0;
|
||||
const readyToPairEventName = "readyToPair";
|
||||
async function initializeSDK() {
|
||||
try {
|
||||
const sdkEntry = logComponentStatusChange("SDK", "Initializing...");
|
||||
const conf = await window.getConfiguration();
|
||||
await window.wc.init(conf.projectId);
|
||||
logComponentStatusChange("SDK", "Initialized", "green", sdkEntry);
|
||||
initEventCount++;
|
||||
} catch (error) {
|
||||
goEcho(`SDK init error: ${error}`);
|
||||
logComponentStatusChange("SDK", "FAIL initializing ${error}", "red", sdkEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate statusObject
|
||||
window.statusq = {
|
||||
channel: {
|
||||
objects: {
|
||||
statusObject: {
|
||||
sdkInitialized: function (error) {
|
||||
window.statusObject_sdkInitialized(error);
|
||||
},
|
||||
onSessionProposal: function (sessionProposal) {
|
||||
window.statusObject_onSessionProposal(JSON.stringify(sessionProposal)).then((success) => {
|
||||
if (!success) {
|
||||
goEcho(`statusObject: onSessionProposal call failed ${sessionProposal.id}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
onSessionRequest: function (sessionRequest) {
|
||||
eventCount++;
|
||||
|
||||
logComponentStatusChange("SDK", `received "session_request" event`, "green");
|
||||
addLogEntry(`Data: ${JSON.stringify(sessionRequest)}`);
|
||||
|
||||
addHtmlEntry(
|
||||
`<button id="acceptSessionButton${eventCount}">Accept</button> <button id="rejectSessionButton${eventCount}">Reject</button>`
|
||||
);
|
||||
|
||||
const acceptSessionButton = document.getElementById(`acceptSessionButton${eventCount}`);
|
||||
const rejectSessionButton = document.getElementById(`rejectSessionButton${eventCount}`);
|
||||
acceptSessionButton.addEventListener("click", function () {
|
||||
const sessionReqEntry = logComponentStatusChange("status-go", `sessionRequest called`, "orange");
|
||||
window.sessionRequest(JSON.stringify(sessionRequest), hashedPasswordInput.value).then((success) => {
|
||||
acceptSessionButton.disabled = true;
|
||||
rejectSessionButton.disabled = true;
|
||||
|
||||
if (success) {
|
||||
logComponentStatusChange("status-go", `sessionRequest OK`, "green", sessionReqEntry);
|
||||
// waiting for "sessionRequestResult" event
|
||||
} else {
|
||||
logComponentStatusChange(
|
||||
"status-go",
|
||||
`sessionRequest call failed for topic ${sessionRequest.topic}`,
|
||||
"red",
|
||||
sessionReqEntry
|
||||
);
|
||||
window.wc.rejectSessionRequest(sessionRequest.topic, sessionRequest.id, true);
|
||||
setStatus(`Session ${sessionRequest.id} rejected, internal error`, "purple");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
rejectSessionButton.addEventListener("click", function () {
|
||||
acceptSessionButton.disabled = true;
|
||||
rejectSessionButton.disabled = true;
|
||||
window.wc.rejectSessionRequest(sessionRequest.topic, sessionRequest.id).then(
|
||||
() => {
|
||||
addLogEntry(`Session ${sessionRequest.id} rejected`);
|
||||
},
|
||||
(err) => {
|
||||
addLogEntry(`Session ${sessionRequest.id} reject error: ${err.message}`, "red");
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
onSessionDelete: function (deletePayload) {
|
||||
goEcho(`statusObject: onSessionDelete ${JSON.stringify(deletePayload)}`);
|
||||
},
|
||||
onSessionExpire: function (expirePayload) {
|
||||
goEcho(`statusObject: onSessionExpire ${JSON.stringify(expirePayload)}`);
|
||||
},
|
||||
onSessionUpdate: function (updatePayload) {
|
||||
goEcho(`statusObject: onSessionUpdate ${JSON.stringify(updatePayload)}`);
|
||||
},
|
||||
onSessionExtend: function (extendPayload) {
|
||||
goEcho(`statusObject: onSessionExtend ${JSON.stringify(extendPayload)}`);
|
||||
},
|
||||
onSessionPing: function (pingPayload) {
|
||||
goEcho(`statusObject: onSessionPing ${JSON.stringify(pingPayload)}`);
|
||||
},
|
||||
onSessionEvent: function (eventPayload) {
|
||||
goEcho(`statusObject: onSessionEvent ${JSON.stringify(eventPayload)}`);
|
||||
},
|
||||
onSessionRequest: function (sessionRequestPayload) {
|
||||
goEcho(`statusObject: onSessionRequest ${JSON.stringify(sessionRequestPayload)}`);
|
||||
},
|
||||
onSessionRequestSent: function (sessionRequestSentPayload) {
|
||||
goEcho(`statusObject: onSessionRequestSent ${JSON.stringify(sessionRequestSentPayload)}`);
|
||||
},
|
||||
onProposalExpire: function (proposalExpirePayload) {
|
||||
goEcho(`statusObject: onProposalExpire ${JSON.stringify(proposalExpirePayload)}`);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var pairLinkInput = null;
|
||||
var pairButton = null;
|
||||
function newPairWorkflow() {
|
||||
// Remove all the previous entries
|
||||
if (logEntries) {
|
||||
for (let i = 0; i < logEntries.length; i++) {
|
||||
logEntries[i].remove();
|
||||
}
|
||||
}
|
||||
logEntries = [];
|
||||
eventCount++;
|
||||
|
||||
// Add session reset and password input
|
||||
addHtmlEntry(`<button id="newSessionButton" style="display: none;">New Session</button>`);
|
||||
newSessionButton = document.getElementById("newSessionButton");
|
||||
newSessionButton.addEventListener("click", function () {
|
||||
newPairWorkflow();
|
||||
});
|
||||
addHtmlEntry(
|
||||
`<input type="text" id="hashedPasswordInput" placeholder="Insert hashed password" value="0x38301fb0b5fcf3aaa4b97c4771bb6c75546e313b4ce7057c51a8cc6a3ace9d7e"/>`
|
||||
);
|
||||
hashedPasswordInput = document.getElementById(`hashedPasswordInput`);
|
||||
|
||||
addHtmlEntry(
|
||||
`<input type="text" id="pairLinkInput" placeholder="Insert pair link" /><button id="pairButton" disabled>Pair</button>`
|
||||
);
|
||||
|
||||
// List existing pairing sessions
|
||||
const pairingsRes = window.wc.getPairings();
|
||||
let pairings = [];
|
||||
if (pairingsRes) {
|
||||
if (!!pairingsRes.error) {
|
||||
goEcho(`getPairings() error: ${pairingsRes.error}`);
|
||||
return;
|
||||
} else if (pairingsRes.result) {
|
||||
pairings = pairingsRes.result;
|
||||
}
|
||||
}
|
||||
if (pairings.length > 0) {
|
||||
addHtmlEntry(`Existing pairings:`, "fuchsia");
|
||||
}
|
||||
for (let i = 0; i < pairings.length; i++) {
|
||||
const p = pairings[i];
|
||||
const disconnectEntry = addHtmlEntry(
|
||||
`[${i + 1}] <span style="color: ${p.active ? "green" : "orange"};">${
|
||||
p.active ? "ACTIVE" : "INACTIVE"
|
||||
}</span> <span class="elide-text">${p.topic}</span>; Expires: ${timestampToStr(
|
||||
p.expiry
|
||||
)} <button id="unpairButton${i}">Disconnect</button>`
|
||||
);
|
||||
const unpairButton = document.getElementById(`unpairButton${i}`);
|
||||
unpairButton.addEventListener("click", function () {
|
||||
const res = window.wc.disconnect(p.topic);
|
||||
if (res && !!res.error) {
|
||||
addLogEntry(`Pairing ${p.topic} disconnect error: ${err.message}`, "red", disconnectEntry);
|
||||
return;
|
||||
}
|
||||
addLogEntry(`Pairing ${p.topic} disconnected`, "green", disconnectEntry);
|
||||
unpairButton.remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Add pairing options
|
||||
pairLinkInput = document.getElementById(`pairLinkInput`);
|
||||
pairButton = document.getElementById(`pairButton`);
|
||||
pairLinkInput.addEventListener("input", function () {
|
||||
pairButton.disabled = !(pairLinkInput.value.length > 0);
|
||||
});
|
||||
pairButton.addEventListener("click", function () {
|
||||
newSessionButton.style.display = "inline";
|
||||
pairButton.disabled = true;
|
||||
pairLinkInput.disabled = true;
|
||||
|
||||
const sdkEntry = logComponentStatusChange("SDK", "Pairing...");
|
||||
const result = window.wc.pair(pairLinkInput.value);
|
||||
if (result && !!result.error) {
|
||||
goEcho("pair() error: ", result.error);
|
||||
logComponentStatusChange("SDK", `Pairing error ${error.message}`, "red", sdkEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
logComponentStatusChange("SDK", "got Pair session proposal", "green", sdkEntry);
|
||||
const goSessionEntry = logComponentStatusChange("GO.pairSessionProposal", "waiting status-go", "pink");
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener(`proposeUserPair`, function (event) {
|
||||
logComponentStatusChange("GO.proposeUserPair", `received "proposeUserPair"`, "green");
|
||||
addLogEntry(JSON.stringify(event.detail.supportedNamespaces));
|
||||
if (!document.getElementById(`acceptPairButton`)) {
|
||||
addHtmlEntry(`<button id="acceptPairButton">Accept</button><button id="rejectPairButton">Reject</button>`);
|
||||
}
|
||||
const acceptPairButton = document.getElementById(`acceptPairButton`);
|
||||
const rejectPairButton = document.getElementById(`rejectPairButton`);
|
||||
const sessionProposal = event.detail.sessionProposal;
|
||||
acceptPairButton.addEventListener("click", function () {
|
||||
const result = window.wc.approvePairSession(sessionProposal, event.detail.supportedNamespaces);
|
||||
if (result && !!result.error) {
|
||||
logComponentStatusChange(
|
||||
"GO.pairSessionProposal",
|
||||
`Pair session ${sessionProposal.id} approve error: ${result.error}`,
|
||||
"red",
|
||||
goSessionEntry
|
||||
);
|
||||
return;
|
||||
}
|
||||
acceptPairButton.remove();
|
||||
rejectPairButton.remove();
|
||||
|
||||
root.controller_recordSuccessfulPairing(JSON.stringify(sessionProposal));
|
||||
|
||||
logComponentStatusChange(
|
||||
"GO.pairSessionProposal",
|
||||
`Pair session ${sessionProposal.id} approved`,
|
||||
"green",
|
||||
goSessionEntry
|
||||
);
|
||||
});
|
||||
rejectPairButton.addEventListener("click", function () {
|
||||
const result = window.wc.rejectPairSession(sessionProposal.id);
|
||||
if (result && !!result.error) {
|
||||
logComponentStatusChange(
|
||||
"GO.pairSessionProposal",
|
||||
`Pair session ${sessionProposal.id} reject error: ${result.error}`,
|
||||
"red",
|
||||
goSessionEntry
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
acceptPairButton.remove();
|
||||
rejectPairButton.remove();
|
||||
|
||||
logComponentStatusChange(
|
||||
"GO.pairSessionProposal",
|
||||
`Pair session ${sessionProposal.id} rejected`,
|
||||
"green",
|
||||
goSessionEntry
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("sessionRequestResult", function (event) {
|
||||
let req = event.detail.sessionRequest;
|
||||
const res = window.wc.respondSessionRequest(req.topic, req.id, event.detail.signed);
|
||||
addLogEntry(`Session ${req.topic} approval accepted`);
|
||||
addHtmlEntry(
|
||||
`</br><a href="https://goerli.etherscan.io/tx/${event.detail.signed}" target="_blank">${event.detail.signed}</a>`
|
||||
);
|
||||
});
|
||||
|
||||
// Add start from scratch option
|
||||
document.addEventListener(readyToPairEventName, function () {
|
||||
newPairWorkflow();
|
||||
});
|
||||
|
||||
async function processGoEvents() {
|
||||
while (true) {
|
||||
try {
|
||||
const event = await window.popNextEvent();
|
||||
switch (event.name) {
|
||||
case "nodeReady":
|
||||
logComponentStatusChange("status-go", "Ready", "green", statusGoEntry);
|
||||
initEventCount++;
|
||||
break;
|
||||
default:
|
||||
// Handle status-go and SDK bootstrap events
|
||||
if (initEventCount == 2) {
|
||||
initEventCount++;
|
||||
document.dispatchEvent(new CustomEvent(readyToPairEventName, {}));
|
||||
} else if (event.name != "") {
|
||||
goEcho(`GO event: ${event.name}`);
|
||||
document.dispatchEvent(new CustomEvent(event.name, { detail: event.payload }));
|
||||
} else {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
goEcho(`GO event error: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processGoEvents();
|
||||
|
||||
// Call the initializeSDK function on page load
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
initializeSDK();
|
||||
});
|
||||
|
||||
function timestampToStr(timestamp) {
|
||||
const date = new Date(timestamp * 1000);
|
||||
const readableDate = date.toLocaleDateString();
|
||||
const readableTime = date.toLocaleTimeString();
|
||||
|
||||
return `${readableDate} - ${readableTime}`;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
<style>
|
||||
.elide-text {
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
transition: max-width 0.3s ease;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.elide-text:hover {
|
||||
max-width: none;
|
||||
background-color: #f0f0f0;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
</html>
|
|
@ -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("<D> 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)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
.integration_tests_config.json
|
|
@ -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')
|
||||
```
|
|
@ -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
|
|
@ -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=
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"hashedPassword": "0x<hashed password as expected by the login call>",
|
||||
"nodeConfigFile": "<node config JSON as expected by the login call>",
|
||||
"dataDir": "<optional user's status data dir (usually status-desktop/Status/data>"
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue