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

added tests for neon core rpc api #195

Merged
merged 9 commits into from
Mar 7, 2024
9 changes: 8 additions & 1 deletion integration/tests/neon_evm/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

from .solana_utils import EvmLoader, create_treasury_pool_address, make_new_user, \
deposit_neon, solana_client, wait_for_account_to_exists
from .utils.constants import NEON_CORE_API_URL
from .utils.constants import NEON_CORE_API_URL, NEON_CORE_API_RPC_URL
from .utils.contract import deploy_contract
from .utils.neon_api_rpc_client import NeonApiRpcClient
from .utils.storage import create_holder
from .types.types import TreasuryPool, Caller, Contract
from .utils.neon_api_client import NeonApiClient
Expand Down Expand Up @@ -162,3 +163,9 @@ def calculator_caller_contract(evm_loader: EvmLoader, operator_keypair: Keypair,
def neon_api_client():
client = NeonApiClient(url=NEON_CORE_API_URL)
return client


@pytest.fixture(scope="session")
def neon_rpc_client():
Copy link
Collaborator

Choose a reason for hiding this comment

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

what difference between NEON_CORE_API_URL and NEON_CORE_API_RPC_URL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is different services now, NEON_CORE_API_URL - old one, rest api (we will delete it in future)
NEON_CORE_API_RPC_URL - rpc service which Vova created

client = NeonApiRpcClient(url=NEON_CORE_API_RPC_URL)
return client
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ def test_get_storage_at(neon_api_client, operator_keypair, user_account, evm_loa
assert storage == zero_array + [0]


def test_get_ether_account_data(neon_api_client, user_account):
result = neon_api_client.get_ether_account_data(user_account.eth_address.hex())['value']
def test_get_balance(neon_api_client, user_account):
result = neon_api_client.get_balance(user_account.eth_address.hex())['value']
assert str(user_account.balance_account_address) == result[0]["solana_address"]
assert solana_client.get_account_info(user_account.solana_account.public_key).value is not None

Expand Down
105 changes: 105 additions & 0 deletions integration/tests/neon_evm/test_neon_core_rpc_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from eth_utils import abi, to_text

from utils.consts import ZERO_HASH, LAMPORT_PER_SOL
from .utils.constants import CHAIN_ID
from .utils.contract import deploy_contract, get_contract_bin
from .solana_utils import solana_client, write_transaction_to_holder_account, deposit_neon
from .utils.ethereum import make_eth_transaction
from .utils.storage import create_holder


def test_get_storage_at(neon_rpc_client, operator_keypair, user_account, evm_loader, treasury_pool):
contract = deploy_contract(operator_keypair, user_account, "hello_world", evm_loader, treasury_pool)
storage = neon_rpc_client.get_storage_at(contract.eth_address.hex(), index='0x0')
zero_array = [0 for _ in range(31)]
assert storage == zero_array + [5]
storage = neon_rpc_client.get_storage_at(contract.eth_address.hex(), index='0x2')
assert storage == zero_array + [0]


def test_get_balance(neon_rpc_client, user_account, sol_client, evm_loader, operator_keypair):
result = neon_rpc_client.get_balance(user_account.eth_address.hex())
assert str(user_account.balance_account_address) == result[0]["solana_address"]
assert solana_client.get_account_info(user_account.solana_account.public_key).value is not None
assert result[0]["status"] == 'Ok'
assert result[0]["balance"] == '0x0'
amount = 100000
deposit_neon(evm_loader, operator_keypair, user_account.eth_address, amount)
result = neon_rpc_client.get_balance(user_account.eth_address.hex())
assert result[0]["balance"] == hex(amount * LAMPORT_PER_SOL)


def test_emulate_transfer(neon_rpc_client, user_account, session_user):
result = neon_rpc_client.emulate(user_account.eth_address.hex(),
session_user.eth_address.hex())
assert result['exit_status'] == 'succeed', f"The 'exit_status' field is not succeed. Result: {result}"
assert result['steps_executed'] == 1, f"Steps executed amount is not 1. Result: {result}"
assert result['used_gas'] > 0, f"Used gas is less than 0. Result: {result}"


def test_emulate_contract_deploy(neon_rpc_client, user_account):
contract_code = get_contract_bin("hello_world")
result = neon_rpc_client.emulate(user_account.eth_address.hex(),
contract=None, data=contract_code)
assert result['exit_status'] == 'succeed', f"The 'exit_status' field is not succeed. Result: {result}"
assert result['steps_executed'] > 100, f"Steps executed amount is wrong. Result: {result}"
assert result['used_gas'] > 0, f"Used gas is less than 0. Result: {result}"


def test_emulate_call_contract_function(neon_rpc_client, operator_keypair, treasury_pool, evm_loader, user_account):
contract = deploy_contract(operator_keypair, user_account, "hello_world", evm_loader, treasury_pool)
data = abi.function_signature_to_4byte_selector('call_hello_world()')

result = neon_rpc_client.emulate(user_account.eth_address.hex(),
contract=contract.eth_address.hex(), data=data)

assert result['exit_status'] == 'succeed', f"The 'exit_status' field is not succeed. Result: {result}"
assert result['steps_executed'] > 0, f"Steps executed amount is 0. Result: {result}"
assert result['used_gas'] > 0, f"Used gas is less than 0. Result: {result}"
assert "Hello World" in to_text(result["result"])


def test_emulate_with_small_amount_of_steps(neon_rpc_client, evm_loader, user_account):
contract_code = get_contract_bin("hello_world")
result = neon_rpc_client.emulate(user_account.eth_address.hex(),
contract=None, data=contract_code, max_steps_to_execute=10)
assert result['message'] == 'Too many steps'


def test_get_contract(neon_rpc_client, rw_lock_contract):
result = neon_rpc_client.get_contract(rw_lock_contract.eth_address.hex())[0]
assert result['solana_address'] == str(rw_lock_contract.solana_address)
assert result['chain_id'] == CHAIN_ID
assert result['code'] != ""


def test_get_holder(neon_rpc_client, operator_keypair, session_user, sender_with_tokens):
holder_acc = create_holder(operator_keypair)
result = neon_rpc_client.get_holder(holder_acc)
assert result["owner"] == str(operator_keypair.public_key)
assert result["status"] == 'Holder'
assert result["tx"] == ZERO_HASH

signed_tx = make_eth_transaction(session_user.eth_address, None, sender_with_tokens, 10)
write_transaction_to_holder_account(signed_tx, holder_acc, operator_keypair)

result = neon_rpc_client.get_holder(holder_acc)
assert result["owner"] == str(operator_keypair.public_key)
assert result["status"] == 'Holder'
assert result["tx"] != ZERO_HASH


def test_get_config(neon_rpc_client):
result = neon_rpc_client.get_config()
assert CHAIN_ID in [item['id'] for item in result["chains"]]
expected_fields = ["NEON_ACCOUNT_SEED_VERSION",
"NEON_EVM_STEPS_LAST_ITERATION_MAX",
"NEON_EVM_STEPS_MIN",
"NEON_GAS_LIMIT_MULTIPLIER_NO_CHAINID",
"NEON_HOLDER_MSG_SIZE",
"NEON_PAYMENT_TO_TREASURE",
"NEON_STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT",
"NEON_TREASURY_POOL_COUNT",
"NEON_TREASURY_POOL_SEED"]
for field in expected_fields:
assert field in result["config"]
7 changes: 5 additions & 2 deletions integration/tests/neon_evm/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
TAG_ACTIVE_STATE = 24
TAG_HOLDER = 52

SOLANA_URL = os.environ.get("SOLANA_URL", "http://localhost:8899")
NEON_CORE_API_URL = os.environ.get("NEON_CORE_API_URL", "http://localhost:8085/api")

SOLANA_URL = os.environ.get("SOLANA_URL", "http://solana:8899")
NEON_CORE_API_URL = os.environ.get("NEON_CORE_API_URL", "http://neon_api:8085/api")
NEON_CORE_API_RPC_URL = os.environ.get("NEON_CORE_API_RPC_URL", "http://neon_core_rpc:3100")

EVM_LOADER = os.environ.get("EVM_LOADER", "53DfF883gyixYNXnM7s5xhdeyV8mVk9T4i2hGV9vG9io")
NEON_TOKEN_MINT_ID: PublicKey = PublicKey(os.environ.get("NEON_TOKEN_MINT", "HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU"))
CHAIN_ID = int(os.environ.get("NEON_CHAIN_ID", 111))
7 changes: 4 additions & 3 deletions integration/tests/neon_evm/utils/neon_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ def emulate(self, sender, contract, data=bytes(), chain_id=CHAIN_ID, value='0x0'
},
"accounts": []
}
print(body)
resp = requests.post(url=f"{self.url}/emulate", json=body, headers=self.headers)
print(resp.text)
if resp.status_code == 200:
return resp.json()["value"]
else:
return resp.json()



def emulate_contract_call(self, sender, contract, function_signature, params=None):
# does not work for tuple in params
data = abi.function_signature_to_4byte_selector(function_signature)
Expand All @@ -43,18 +42,20 @@ def emulate_contract_call(self, sender, contract, function_signature, params=Non
data += eth_abi.encode(types, params)
return self.emulate(sender, contract, data)


def get_storage_at(self, contract_id, index="0x0"):
body = {
"contract": contract_id,
"index": index
}
return requests.post(url=f"{self.url}/storage", json=body, headers=self.headers).json()


def get_holder(self, public_key):
body = {"pubkey": f"{public_key}"}
return requests.post(url=f"{self.url}/holder", json=body, headers=self.headers).json()

def get_ether_account_data(self, ether, chain_id = CHAIN_ID):
def get_balance(self, ether, chain_id = CHAIN_ID):
body = {
"account": [
{ "address": ether, "chain_id": chain_id }
Expand Down
59 changes: 59 additions & 0 deletions integration/tests/neon_evm/utils/neon_api_rpc_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import requests

from .constants import CHAIN_ID


class NeonApiRpcClient:
def __init__(self, url):
self.url = url
self.headers = {"Content-Type": "application/json"}

def post(self, method, params):
body = {
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": [params],
}
resp = requests.post(url=f"{self.url}", json=body, headers=self.headers).json()
if "result" in resp:
return resp['result']
else:
return resp['error']

def get_storage_at(self, contract, index="0x0"):
params = {"contract": contract, "index": index}
return self.post("get_storage_at", params)

def get_balance(self, ether, chain_id=CHAIN_ID):
params = {"account": [{"address": ether, "chain_id": chain_id}]}

return self.post("balance", params)

def emulate(self, sender, contract, data=bytes(), chain_id=CHAIN_ID, value='0x0', max_steps_to_execute=500000):
if isinstance(data, bytes):
data = data.hex()
params = {
"step_limit": max_steps_to_execute,
"tx": {
"from": sender,
"to": contract,
"data": data,
"chain_id": chain_id,
"value": value
},
"accounts": []
}
return self.post("emulate", params)

def get_contract(self, address):
params = {"contract": address}
return self.post("contract", params)

def get_holder(self, pubkey):
params = {"pubkey": str(pubkey)}
return self.post("holder", params)

def get_config(self):
params = {}
return self.post("config", params)
Loading