Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat_: sending transaction improvements #5807

Merged
merged 6 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions integration-tests/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
},
"Networks": [
{
"ChainID": 31337,
"RPCURL": "http://anvil:8545"
}
"ChainID": 31337,
"ChainName": "Anvil",
"DefaultRPCURL": "http://anvil:8545",
"RPCURL": "http://anvil:8545",
"ShortName": "eth",
"NativeCurrencyName": "Ether",
"NativeCurrencySymbol": "ETH",
"NativeCurrencyDecimals": 18,
"IsTest": false,
"Layer": 1,
"Enabled": true
}
]
}
10 changes: 8 additions & 2 deletions integration-tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ def pytest_addoption(parser):
help="",
default="ws://0.0.0.0:8354",
)
parser.addoption(
"--anvil_url",
action="store",
help="",
default="http://0.0.0.0:8545",
)
parser.addoption(
"--password",
action="store",
Expand All @@ -35,11 +41,11 @@ class Account():
private_key: str

user_1 = Account(
address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
address="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
private_key="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
)
user_2 = Account(
address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
address="0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
private_key="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
)

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/docker-compose.anvil.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
image: ghcr.io/foundry-rs/foundry:latest
platform: linux/amd64
command:
- anvil --host 0.0.0.0
- anvil --host 0.0.0.0 --block-time 2

deploy-sntv2:
platform: linux/amd64
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/docker-compose.test.status-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ services:
"--password", "Strong12345",
"--dir", "/tmp/status-go-data", # Keep in sync with `config.json/DataDir` value. Later this arg will not be needed.
]
ports:
- 3333:3333
# - 8354:8354 # use for local debbuging only
healthcheck:
test: ["CMD-SHELL", "curl -X POST --data '{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":1}' -H 'Content-Type: application/json' http://0.0.0.0:3333 || exit 1"]
interval: 5s
Expand Down Expand Up @@ -72,6 +75,8 @@ services:
"-m", "wallet",
"--rpc_url=http://status-go:3333",
"--rpc_url_2=http://status-go-no-funds:3333",
"--anvil_url=http://anvil:8545",
"--ws_url=ws://status-go:8354",
"--junitxml=/tests-rpc/reports/report.xml",
]
volumes:
Expand Down
54 changes: 34 additions & 20 deletions integration-tests/tests/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import threading
import logging
import jsonschema
import time
import requests
from conftest import option, user_1, user_2

Expand All @@ -16,9 +17,11 @@ def _try_except_JSONDecodeError_KeyError(self, response, key: str):
try:
return response.json()[key]
except json.JSONDecodeError:
raise AssertionError(f"Invalid JSON in response: {response.content}")
raise AssertionError(
f"Invalid JSON in response: {response.content}")
except KeyError:
raise AssertionError(f"Key '{key}' not found in the JSON response.")
raise AssertionError(
f"Key '{key}' not found in the JSON response: {response.content}")

def verify_is_valid_json_rpc_response(self, response, _id=None):
assert response.status_code == 200
Expand All @@ -40,7 +43,6 @@ def verify_is_json_rpc_error(self, response):
assert response.content
self._try_except_JSONDecodeError_KeyError(response, "error")


def rpc_request(self, method, params=[], _id=None, client=None, url=None):
client = client if client else requests.Session()
url = url if url else option.rpc_url
Expand All @@ -60,28 +62,27 @@ def verify_json_schema(self, response, method):
schema=json.load(schema))



class TransactionTestCase(RpcTestCase):


def wallet_create_multi_transaction(self, **kwargs):
method = "wallet_createMultiTransaction"
transferTx_data = {
"data": "",
"from": user_1.address,
"gas": "0x5BBF",
"input": "",
"maxFeePerGas": "0xbcc0f04fd",
"maxPriorityFeePerGas": "0xbcc0f04fd",
"to": user_2.address,
"type": "0x02",
"value": "0x5af3107a4000",
}
"data": "",
"from": user_1.address,
"gas": "0x5BBF",
"input": "",
"maxFeePerGas": "0xbcc0f04fd",
"maxPriorityFeePerGas": "0xbcc0f04fd",
"to": user_2.address,
"type": "0x02",
"value": "0x5af3107a4000",
}
for key, new_value in kwargs.items():
if key in transferTx_data:
transferTx_data[key] = new_value
else:
print(f"Warning: The key '{key}' does not exist in the transferTx parameters and will be ignored.")
print(
f"Warning: The key '{key}' does not exist in the transferTx parameters and will be ignored.")
params = [
{
"fromAddress": user_1.address,
Expand Down Expand Up @@ -116,10 +117,23 @@ def setup_method(self):

class SignalTestCase(RpcTestCase):

received_signals = []
await_signals = []
received_signals = {}

def on_message(self, ws, signal):
signal = json.loads(signal)
if signal.get("type") in self.await_signals:
self.received_signals[signal["type"]] = signal

def _on_message(self, ws, signal):
self.received_signals.append(signal)
def wait_for_signal(self, signal_type, timeout=10):
start_time = time.time()
while signal_type not in self.received_signals:
time_passed = time.time() - start_time
if time_passed >= timeout:
raise TimeoutError(
f"Signal {signal_type} is not received in {timeout} seconds")
time.sleep(0.5)
return self.received_signals[signal_type]
Comment on lines +128 to +136
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antdanchenko I think this should be a bit different.

If in the 0.5 seconds a few of same signals received, you'll randomly get one of them, instead of getting the first one received. So this introduces a flakiness right away.

Also, so far there's need to store all received_signals. In most (if not 100%) of cases you'll only need to wait for 1 specific signal, first received.


This doesn't need to be changed in this PR, but just raising your awareness.

Copy link
Contributor

@antdanchenko antdanchenko Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method just checks (every 0.5) if there is an expected signal in the received_signals list,

the list itself is being appended by every signal that matches self.await_signals here https://github.com/status-im/status-go/pull/5807/files#diff-396e333ecb0055730c471697ae8e74841d20f7308a2f1f9ad03fd49851518bf6R123


def _on_error(self, ws, error):
logging.info(f"Error: {error}")
Expand All @@ -134,7 +148,7 @@ def _connect(self):
self.url = f"{option.ws_url}/signals"

ws = websocket.WebSocketApp(self.url,
on_message=self._on_message,
on_message=self.on_message,
on_error=self._on_error,
on_close=self._on_close)

Expand Down
120 changes: 120 additions & 0 deletions integration-tests/tests/test_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import pytest
import time
import uuid
from conftest import user_1, user_2, option
from test_cases import SignalTestCase


@pytest.mark.rpc
@pytest.mark.transaction
@pytest.mark.wallet
class TestTransactionFromRoute(SignalTestCase):

await_signals = [
"wallet.suggested.routes",
"wallet.router.sign-transactions",
"wallet.router.sending-transactions-started",
"wallet.transaction.status-changed",
"wallet.router.transactions-sent"
]

def test_tx_from_route(self):

_uuid = str(uuid.uuid4())
amount_in = "0xde0b6b3a7640000"

method = "wallet_getSuggestedRoutesAsync"
params = [
{
"uuid": _uuid,
"sendType": 0,
"addrFrom": user_1.address,
"addrTo": user_2.address,
"amountIn": amount_in,
"amountOut": "0x0",
"tokenID": "ETH",
"tokenIDIsOwnerToken": False,
"toTokenID": "",
"disabledFromChainIDs": [10, 42161],
"disabledToChainIDs": [10, 42161],
"gasFeeMode": 1,
"fromLockedAmount": {}
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)
igor-sirotin marked this conversation as resolved.
Show resolved Hide resolved

routes = self.wait_for_signal("wallet.suggested.routes")
assert routes['event']['Uuid'] == _uuid

method = "wallet_buildTransactionsFromRoute"
params = [
{
"uuid": _uuid,
"slippagePercentage": 0
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

self.wait_for_signal("wallet.router.sign-transactions")

assert self.received_signals[
'wallet.router.sign-transactions']['event']['signingDetails']['signOnKeycard'] == False
transaction_hashes = self.received_signals[
'wallet.router.sign-transactions']['event']['signingDetails']['hashes']

assert transaction_hashes, "Transaction hashes are empty!"

tx_signatures = {}

for hash in transaction_hashes:

method = "wallet_signMessage"
params = [
hash,
user_1.address,
option.password
]

response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

if response.json()["result"].startswith("0x"):
tx_signature = response.json()["result"][2:]

signature = {
"r": tx_signature[:64],
"s": tx_signature[64:128],
"v": tx_signature[128:]
}

tx_signatures[hash] = signature

method = "wallet_sendRouterTransactionsWithSignatures"
params = [
{
"uuid": _uuid,
"Signatures": tx_signatures
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

tx_status = self.wait_for_signal("wallet.transaction.status-changed")


assert tx_status["event"]["chainId"] == 31337
assert tx_status["event"]["status"] == "Success"
tx_hash = tx_status["event"]["hash"]

method = "eth_getTransactionByHash"
params = [tx_hash]

response = self.rpc_request(method, params, url=option.anvil_url)
self.verify_is_valid_json_rpc_response(response)
tx_details = response.json()["result"]

assert tx_details["value"] == amount_in
assert tx_details["to"] == user_2.address
assert tx_details["from"] == user_1.address
3 changes: 1 addition & 2 deletions integration-tests/tests/test_wallet_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ class TestRpc(RpcTestCase):
("wallet_getEthereumChains", []),
("wallet_getTokenList", []),
("wallet_getCryptoOnRamps", []),
("wallet_getCachedCurrencyFormats", []),
("wallet_fetchAllCurrencyFormats", [])
("wallet_getCachedCurrencyFormats", [])
],
)
def test_(self, method, params):
Expand Down
Loading