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

Check that the user's account balance is less than required and raise… #572

Merged
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
1 change: 1 addition & 0 deletions proxy/docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ services:
PYTH_MAPPING_ACCOUNT: BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2
MIN_OPERATOR_BALANCE_TO_WARN: 4565760000 # = 913152000 * 5 (5 storage accounts) = 4.56576 SOL
MIN_OPERATOR_BALANCE_TO_ERR: 913152000 # = solana rent 131072 (= Rent-exempt minimum: 0.913152 SOL) SOLs to create a storage
MINIMAL_GAS_PRICE: 1
hostname: proxy
depends_on:
postgres:
Expand Down
10 changes: 7 additions & 3 deletions proxy/plugin/gas_price_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ def __init__(self, solana_client, pyth_mapping_acc) -> None:
self.recent_sol_price_update_time = None
self.min_gas_price = None

def env_min_gas_price(self):
if MINIMAL_GAS_PRICE is not None:
return MINIMAL_GAS_PRICE

def update_mapping(self):
if self.mapping_account is not None:
self.pyth_network_client.update_mapping(self.mapping_account)

def get_min_gas_price(self):
if MINIMAL_GAS_PRICE is not None:
return MINIMAL_GAS_PRICE
if self.env_min_gas_price() is not None:
return self.env_min_gas_price()
self.try_update_gas_price()
return self.min_gas_price

Expand Down Expand Up @@ -60,4 +64,4 @@ def start_update_gas_price(self, cur_time):
raise Exception('Failed to estimate gas price. Try again later')

self.info(f'Will retry getting price after {GET_SOL_PRICE_RETRY_INTERVAL} seconds')
time.sleep(GET_SOL_PRICE_RETRY_INTERVAL)
time.sleep(GET_SOL_PRICE_RETRY_INTERVAL)
12 changes: 11 additions & 1 deletion proxy/plugin/solana_rest_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,19 @@ def eth_sendRawTransaction(self, rawTrx):
min_gas_price = self.gas_price_calculator.get_min_gas_price()

if trx.gasPrice < min_gas_price:
raise RuntimeError("The transaction gasPrice is less then the minimum allowable value" +
raise RuntimeError("The transaction gasPrice is less than the minimum allowable value" +
f"({trx.gasPrice}<{min_gas_price})")

user_balance = int(self.eth_getBalance('0x' + trx.sender(), 'latest'), 16)
fee = trx.gasPrice * trx.gasLimit
required_balance = fee + trx.value
if user_balance < required_balance:
raise RuntimeError("The account balance is less than required: " +
f"Account {trx.sender()}; balance = {user_balance}; " +
f"gasPrice = {trx.gasPrice}; gasLimit = {trx.gasLimit}; " +
f"fee = {fee}; value = {trx.value}; " +
f"required_balance = {required_balance}; ")

eth_signature = '0x' + trx.hash_signed().hex()

try:
Expand Down
6 changes: 3 additions & 3 deletions proxy/run-set-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if [ "$CONFIG" == "ci" ]; then
[[ -z "$EXTRA_GAS" ]] && export EXTRA_GAS=100000
[[ -z "$NEON_CLI_TIMEOUT" ]] && export NEON_CLI_TIMEOUT="0.5"
[[ -z "$CONTINUE_COUNT_FACTOR" ]] && export CONTINUE_COUNT_FACTOR="3"
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE=0
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE=1
[[ -z "$POSTGRES_HOST" ]] && export POSTGRES_HOST="postgres"
[[ -z "$CANCEL_TIMEOUT" ]] && export CANCEL_TIMEOUT=10
[[ -z "$RETRY_ON_FAIL" ]] && export RETRY_ON_FAIL=10
Expand All @@ -21,7 +21,7 @@ elif [ "$CONFIG" == "local" ]; then
[[ -z "$SOLANA_URL" ]] && export SOLANA_URL="http://localhost:8899"
[[ -z "$EXTRA_GAS" ]] && export EXTRA_GAS=0
[[ -z "$NEON_CLI_TIMEOUT" ]] && export NEON_CLI_TIMEOUT="0.9"
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE=0
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE=1
[[ -z "$POSTGRES_HOST" ]] && export POSTGRES_HOST="localhost"
[[ -z "$CANCEL_TIMEOUT" ]] && export CANCEL_TIMEOUT=10
[[ -z "$RETRY_ON_FAIL" ]] && export RETRY_ON_FAIL=10
Expand All @@ -47,7 +47,7 @@ elif [ "$CONFIG" == "testnet" ]; then
[[ -z "$EVM_LOADER" ]] && export EVM_LOADER=eeLSJgWzzxrqKv1UxtRVVH8FX3qCQWUs9QuAjJpETGU
[[ -z "$EXTRA_GAS" ]] && export EXTRA_GAS=90000
[[ -z "$NEON_CLI_TIMEOUT" ]] && export NEON_CLI_TIMEOUT="15"
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE="1"
[[ -z "$MINIMAL_GAS_PRICE" ]] && export MINIMAL_GAS_PRICE=1
[[ -z "$POSTGRES_HOST" ]] && export POSTGRES_HOST="localhost"
[[ -z "$CANCEL_TIMEOUT" ]] && export CANCEL_TIMEOUT=60
[[ -z "$RETRY_ON_FAIL" ]] && export RETRY_ON_FAIL=10
Expand Down
2 changes: 2 additions & 0 deletions proxy/testing/test_airdropper_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import json

from proxy.testing.testing_helpers import request_airdrop

MAX_AIRDROP_WAIT_TIME = 45
EVM_LOADER_ID = PublicKey(EVM_LOADER_ID)
Expand All @@ -28,6 +29,7 @@
proxy = Web3(Web3.HTTPProvider(PROXY_URL))
admin = proxy.eth.account.create('neonlabsorg/proxy-model.py/issues/344/admin20')
proxy.eth.default_account = admin.address
request_airdrop(admin.address)


# Helper function calculating solana address and nonce from given NEON(Ethereum) address
Expand Down
4 changes: 2 additions & 2 deletions proxy/testing/test_create_account_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def deploy_contract(self):
nonce=proxy.eth.get_transaction_count(eth_account.address),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to='',
value=0,
data=storage.bytecode),
Expand All @@ -72,7 +72,7 @@ def transfer(self, target_account, value):
nonce=proxy.eth.get_transaction_count(eth_account.address),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=bytes(target_account),
value=value),
eth_account.key
Expand Down
4 changes: 4 additions & 0 deletions proxy/testing/test_erc20_wrapper_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
from proxy.common_neon.neon_instruction import NeonInstruction
from solana.rpc.types import TokenAccountOpts

from proxy.testing.testing_helpers import request_airdrop

proxy_url = os.environ.get('PROXY_URL', 'http://127.0.0.1:9090/solana')
solana_url = os.environ.get("SOLANA_URL", "http://127.0.0.1:8899")
proxy = Web3(Web3.HTTPProvider(proxy_url))
admin = proxy.eth.account.create('issues/neonlabsorg/proxy-model.py/197/admin')
user = proxy.eth.account.create('issues/neonlabsorg/proxy-model.py/197/user')
proxy.eth.default_account = admin.address
request_airdrop(admin.address)
request_airdrop(user.address)

NAME = 'NEON'
SYMBOL = 'NEO'
Expand Down
5 changes: 4 additions & 1 deletion proxy/testing/test_eth_estimateGas.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from web3 import Web3
from solcx import compile_source

from proxy.testing.testing_helpers import request_airdrop

EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "0"))
proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana')
proxy = Web3(Web3.HTTPProvider(proxy_url))
Expand Down Expand Up @@ -32,6 +34,7 @@ def setUpClass(cls):
print("\n\nhttps://github.com/neonlabsorg/proxy-model.py/issues/487")
print('eth_account.address:', eth_account.address)
print('eth_account.key:', eth_account.key.hex())
request_airdrop(eth_account.address)
cls.deploy_counter_487_solidity_contract(cls)

def deploy_counter_487_solidity_contract(self):
Expand All @@ -42,7 +45,7 @@ def deploy_counter_487_solidity_contract(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to='',
value=0,
data=counter.bytecode),
Expand Down
5 changes: 4 additions & 1 deletion proxy/testing/test_eth_getLogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from web3 import Web3
from solcx import compile_source

from proxy.testing.testing_helpers import request_airdrop

SEED = 'https://github.com/neonlabsorg/proxy-model.py/issues/210'
EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "0"))
proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana')
Expand Down Expand Up @@ -50,6 +52,7 @@ def setUpClass(cls):
print(SEED)
print('eth_account.address:', eth_account.address)
print('eth_account.key:', eth_account.key.hex())
request_airdrop(eth_account.address)

cls.block_hashes = []
cls.topics = []
Expand All @@ -75,7 +78,7 @@ def deploy_contract(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to='',
value=0,
data=storage.bytecode),
Expand Down
56 changes: 45 additions & 11 deletions proxy/testing/test_eth_sendRawTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

SUBSTRING_LOG_ERR_147 = 'Invalid Ethereum transaction nonce:'

SUBSTRING_ERR_484 = 'The account balance is less than required:'

STORAGE_SOLIDITY_SOURCE_147 = '''
pragma solidity >=0.7.0 <0.9.0;
/**
Expand Down Expand Up @@ -97,7 +99,7 @@ def deploy_storage_147_solidity_contract(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=2000000000,
to='',
value=0,
data=storage.bytecode),
Expand Down Expand Up @@ -127,7 +129,7 @@ def deploy_test_185_solidity_contract(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to='',
value=0,
data=test_185_solidity_contract.bytecode),
Expand Down Expand Up @@ -186,7 +188,7 @@ def test_02_execute_with_right_nonce(self):
def test_03_execute_with_low_gas(self):
print("\ntest_03_execute_with_low_gas")
right_nonce = proxy.eth.get_transaction_count(proxy.eth.default_account)
trx_store = self.storage_contract.functions.store(148).buildTransaction({'nonce': right_nonce, 'gasPrice': 1})
trx_store = self.storage_contract.functions.store(148).buildTransaction({'nonce': right_nonce, 'gasPrice': 1000000000})
print('trx_store:', trx_store)
trx_store['gas'] = trx_store['gas'] - 2 - EXTRA_GAS # less than estimated
print('trx_store:', trx_store)
Expand Down Expand Up @@ -241,6 +243,8 @@ def test_05_transfer_one_gwei(self):
eth_account_bob = proxy.eth.account.create('bob')
print('eth_account_alice.address:', eth_account_alice.address)
print('eth_account_bob.address:', eth_account_bob.address)
request_airdrop(eth_account_alice.address)
request_airdrop(eth_account_bob.address)

if True:
print("add funds to alice and bob")
Expand All @@ -250,7 +254,7 @@ def test_05_transfer_one_gwei(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_alice.address,
value=eth_utils.denoms.gwei),
eth_account.key
Expand All @@ -267,7 +271,7 @@ def test_05_transfer_one_gwei(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_bob.address,
value=eth_utils.denoms.gwei),
eth_account.key
Expand All @@ -289,7 +293,7 @@ def test_05_transfer_one_gwei(self):
nonce=proxy.eth.get_transaction_count(eth_account_alice.address),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_bob.address,
value=eth_utils.denoms.gwei),
eth_account_alice.key
Expand All @@ -305,7 +309,7 @@ def test_05_transfer_one_gwei(self):
bob_balance_after_transfer = proxy.eth.get_balance(eth_account_bob.address)
print('alice_balance_after_transfer:', alice_balance_after_transfer)
print('bob_balance_after_transfer:', bob_balance_after_transfer)
self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - eth_utils.denoms.gwei)
self.assertLessEqual(alice_balance_after_transfer, alice_balance_before_transfer - eth_utils.denoms.gwei)
self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + eth_utils.denoms.gwei)

# @unittest.skip("a.i.")
Expand All @@ -316,6 +320,8 @@ def test_06_transfer_one_and_a_half_gweis(self):
eth_account_bob = proxy.eth.account.create('bob')
print('eth_account_alice.address:', eth_account_alice.address)
print('eth_account_bob.address:', eth_account_bob.address)
request_airdrop(eth_account_alice.address)
request_airdrop(eth_account_bob.address)

if True:
print("add funds to alice and bob")
Expand All @@ -325,7 +331,7 @@ def test_06_transfer_one_and_a_half_gweis(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_alice.address,
value=eth_utils.denoms.gwei),
eth_account.key
Expand All @@ -342,7 +348,7 @@ def test_06_transfer_one_and_a_half_gweis(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_bob.address,
value=eth_utils.denoms.gwei),
eth_account.key
Expand All @@ -365,7 +371,7 @@ def test_06_transfer_one_and_a_half_gweis(self):
nonce=proxy.eth.get_transaction_count(eth_account_alice.address),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to=eth_account_bob.address,
value=one_and_a_half_gweis),
eth_account_alice.key
Expand All @@ -383,7 +389,7 @@ def test_06_transfer_one_and_a_half_gweis(self):
print('bob_balance_after_transfer:', bob_balance_after_transfer)
print('check https://github.com/neonlabsorg/neon-evm/issues/210')
print('one_gwei:', eth_utils.denoms.gwei)
self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - eth_utils.denoms.gwei)
self.assertLessEqual(alice_balance_after_transfer, alice_balance_before_transfer - eth_utils.denoms.gwei)
self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + eth_utils.denoms.gwei)

@unittest.skip("a.i.")
Expand Down Expand Up @@ -456,5 +462,33 @@ def test_get_storage_at(self):
print('eth_getStorageAt not_a_contract_address address => ', value_received.hex())
self.assertEqual(int.from_bytes(value_received, byteorder='big'), 0)

# @unittest.skip("a.i.")
def test_08_execute_with_huge_gas(self):
print("\ntest_08_execute_with_huge_gas_limit")
nonce = proxy.eth.get_transaction_count(proxy.eth.default_account)
trx_store = self.storage_contract.functions.store(147).buildTransaction({'gas': 987654321987654321, 'nonce': nonce, 'gasPrice': 1000000000})
print('trx_store:', trx_store)
trx_store_signed = proxy.eth.account.sign_transaction(trx_store, eth_account.key)
print('trx_store_signed:', trx_store_signed)
try:
trx_store_hash = proxy.eth.send_raw_transaction(trx_store_signed.rawTransaction)
print('trx_store_hash:', trx_store_hash)
self.assertTrue(False)
except Exception as e:
print('type(e):', type(e))
print('e:', e)
import json
response = json.loads(str(e).replace('\'', '\"').replace('None', 'null'))
print('response:', response)
print('code:', response['code'])
self.assertEqual(response['code'], -32000)
substring_err_484 = SUBSTRING_ERR_484
print('substring_err_484:', substring_err_484)
print('message:', response['message'])
message = response['message']
self.assertTrue(substring_err_484 in message)
self.assertGreater(len(message), len(substring_err_484))


if __name__ == '__main__':
unittest.main()
13 changes: 9 additions & 4 deletions proxy/testing/test_gas_price_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
from proxy.indexer.pythnetwork import PythNetworkClient
from proxy.plugin.gas_price_calculator import GasPriceCalculator
from solana.rpc.api import Client as SolanaClient
from unittest.mock import patch, call
from unittest.mock import patch, call, Mock
from decimal import Decimal
from ..environment import NEON_PRICE_USD, OPERATOR_FEE, GET_SOL_PRICE_MAX_RETRIES, SOL_PRICE_UPDATE_INTERVAL
from ..environment import NEON_PRICE_USD, OPERATOR_FEE, GET_SOL_PRICE_MAX_RETRIES, SOL_PRICE_UPDATE_INTERVAL, \
MINIMAL_GAS_PRICE

MINIMAL_GAS_PRICE = None

class TestGasPriceCalculator(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.mapping_account = PublicKey('BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2') # only for devnet
cls.mapping_account = PublicKey('BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2') # only for devnet
cls.solana_url = "https://api.devnet.solana.com" # devnet
cls.solana_client = SolanaClient(cls.solana_url)
cls.testee = GasPriceCalculator(cls.solana_client, cls.mapping_account)
cls.testee.env_min_gas_price = Mock()
cls.testee.env_min_gas_price.return_value = None


def setUp(self) -> None:
Expand Down Expand Up @@ -137,4 +142,4 @@ def test_success_get_price_time_intervals(self, mock_get_price, mock_get_current
self.assertEqual(gas_price3, expected_price2)

mock_get_current_time.assert_has_calls([call()] * 3)
mock_get_price.assert_has_calls([call('Crypto.SOL/USD')] * 2)
mock_get_price.assert_has_calls([call('Crypto.SOL/USD')] * 2)
2 changes: 1 addition & 1 deletion proxy/testing/test_indexer_work.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def deploy_contract(self):
nonce=proxy.eth.get_transaction_count(proxy.eth.default_account),
chainId=proxy.eth.chain_id,
gas=987654321,
gasPrice=0,
gasPrice=1000000000,
to='',
value=0,
data=storage.bytecode),
Expand Down
Loading