From 505653656a5406d64592c4fa2ebcf8ff1aed8edd Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 15:45:44 +0500 Subject: [PATCH 01/95] constants and utils --- proxy/common/constants.py | 2 ++ proxy/common/utils.py | 12 ++++++++++++ proxy/test_eth_sendRawTransaction.py | 28 ++++++++++++---------------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/proxy/common/constants.py b/proxy/common/constants.py index 1479523a7..48c35a691 100644 --- a/proxy/common/constants.py +++ b/proxy/common/constants.py @@ -86,3 +86,5 @@ PLUGIN_DEVTOOLS_PROTOCOL = 'proxy.http.inspector.DevtoolsProtocolPlugin' PLUGIN_DASHBOARD = 'proxy.dashboard.dashboard.ProxyDashboard' PLUGIN_INSPECT_TRAFFIC = 'proxy.dashboard.inspect_traffic.InspectTrafficPlugin' + +GWEI_PER_ETH_COUNT = 1_000_000_000 diff --git a/proxy/common/utils.py b/proxy/common/utils.py index ecdc4e9fe..053aa92ca 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -208,3 +208,15 @@ def get_available_port() -> int: sock.bind(('', 0)) _, port = sock.getsockname() return int(port) + + +def get_from_dict(src: Dict, *path) -> Optional[Any]: + """Provides smart getting values from python dictionary""" + val = src + for key in path: + if not isinstance(val, dict): + return None + val = val.get(key) + if val is None: + return None + return val diff --git a/proxy/test_eth_sendRawTransaction.py b/proxy/test_eth_sendRawTransaction.py index 35ac90651..62a035e39 100644 --- a/proxy/test_eth_sendRawTransaction.py +++ b/proxy/test_eth_sendRawTransaction.py @@ -2,6 +2,7 @@ import os from web3 import Web3 from solcx import install_solc +from .common.constants import GWEI_PER_ETH_COUNT # install_solc(version='latest') install_solc(version='0.7.0') @@ -237,8 +238,6 @@ def test_04_execute_with_bad_nonce(self): def test_05_transfer_one_gwei(self): print("\ntest_05_transfer_one_gwei") - one_gwei = 1_000_000_000 - eth_account_alice = proxy.eth.account.create('alice') eth_account_bob = proxy.eth.account.create('bob') print('eth_account_alice.address:', eth_account_alice.address) @@ -254,7 +253,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_alice.address, - value=one_gwei), + value=GWEI_PER_ETH_COUNT), eth_account.key ) @@ -271,7 +270,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=one_gwei), + value=GWEI_PER_ETH_COUNT), eth_account.key ) @@ -285,7 +284,7 @@ def test_05_transfer_one_gwei(self): bob_balance_before_transfer = proxy.eth.get_balance(eth_account_bob.address) print('alice_balance_before_transfer:', alice_balance_before_transfer) print('bob_balance_before_transfer:', bob_balance_before_transfer) - print('one_gwei:', one_gwei) + print('one_gwei:', GWEI_PER_ETH_COUNT) trx_transfer = proxy.eth.account.sign_transaction(dict( nonce=proxy.eth.get_transaction_count(eth_account_alice.address), @@ -293,7 +292,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=one_gwei), + value=GWEI_PER_ETH_COUNT), eth_account_alice.key ) @@ -307,8 +306,8 @@ 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 - one_gwei) - self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + one_gwei) + self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - GWEI_PER_ETH_COUNT) + self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + GWEI_PER_ETH_COUNT) # @unittest.skip("a.i.") def test_06_transfer_one_and_a_half_gweis(self): @@ -319,8 +318,6 @@ def test_06_transfer_one_and_a_half_gweis(self): print('eth_account_alice.address:', eth_account_alice.address) print('eth_account_bob.address:', eth_account_bob.address) - one_gwei = 1_000_000_000 - if True: print("add funds to alice and bob") @@ -331,7 +328,7 @@ def test_06_transfer_one_and_a_half_gweis(self): gas=987654321, gasPrice=0, to=eth_account_alice.address, - value=one_gwei), + value=GWEI_PER_ETH_COUNT), eth_account.key ) @@ -348,7 +345,7 @@ def test_06_transfer_one_and_a_half_gweis(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=one_gwei), + value=GWEI_PER_ETH_COUNT), eth_account.key ) @@ -386,10 +383,9 @@ def test_06_transfer_one_and_a_half_gweis(self): print('alice_balance_after_transfer:', alice_balance_after_transfer) print('bob_balance_after_transfer:', bob_balance_after_transfer) print('check https://github.com/neonlabsorg/neon-evm/issues/210') - one_gwei = 1_000_000_000 - print('one_gwei:', one_gwei) - self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - one_gwei) - self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + one_gwei) + print('one_gwei:', GWEI_PER_ETH_COUNT) + self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - GWEI_PER_ETH_COUNT) + self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + GWEI_PER_ETH_COUNT) @unittest.skip("a.i.") def test_07_execute_long_transaction(self): From 95cfd3a0f7bc6ac33ab66838c12e74e102c872a0 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 15:50:20 +0500 Subject: [PATCH 02/95] solana rest api tools imports --- proxy/plugin/solana_rest_api_tools.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index a46fab3cd..34038990c 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -10,26 +10,32 @@ import time from datetime import datetime from hashlib import sha256 -from typing import NamedTuple +from typing import NamedTuple, Optional, Union, Dict +from enum import Enum import rlp from base58 import b58decode, b58encode from construct import Bytes, Int8ul, Int32ul, Int64ul from construct import Struct as cStruct from eth_keys import keys as eth_keys from sha3 import keccak_256 -from solana._layouts.system_instructions import SYSTEM_INSTRUCTIONS_LAYOUT -from solana._layouts.system_instructions import InstructionType as SystemInstructionType +from web3.auto import w3 + +from solana.account import Account as SolanaAccount from solana.blockhash import Blockhash from solana.publickey import PublicKey -from solana.rpc.api import Client, SendTransactionError +from solana.rpc.api import Client as SolanaClient, SendTransactionError from solana.rpc.commitment import Commitment, Confirmed from solana.rpc.types import TxOpts from solana.sysvar import * from solana.transaction import AccountMeta, Transaction, TransactionInstruction +from solana._layouts.system_instructions import SYSTEM_INSTRUCTIONS_LAYOUT +from solana._layouts.system_instructions import InstructionType as SystemInstructionType + from spl.token.constants import ACCOUNT_LEN, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID from spl.token.instructions import get_associated_token_address, create_associated_token_account, transfer2, Transfer2Params -from web3.auto import w3 +from ..common.utils import get_from_dict +from ..common.constants import GWEI_PER_ETH_COUNT from .eth_proto import Trx logger = logging.getLogger(__name__) From c10801375f8d6ac76119219e4ad568fefa1213f3 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 15:51:45 +0500 Subject: [PATCH 03/95] comments and tabs --- proxy/plugin/solana_rest_api.py | 4 +-- proxy/plugin/solana_rest_api_tools.py | 39 +++++++++++++++++---------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 115ef7d16..45f328e72 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -139,7 +139,6 @@ def process_block_tag(self, tag): slot = int(tag, 16) return slot - def eth_blockNumber(self): slot = self.client.get_slot(commitment=Confirmed)['result'] logger.debug("eth_blockNumber %s", hex(slot)) @@ -467,6 +466,7 @@ def eth_sendRawTransaction(self, rawTrx): logger.debug("eth_sendRawTransaction type(err):%s, Exception:%s", type(err), err) raise + class JsonEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, bytearray): @@ -536,7 +536,6 @@ def test_transferTokens(self): self.assertTrue(receiptId in block['transactions']) - class SolanaProxyPlugin(HttpWebServerBasePlugin): """Extend in-built Web Server to add Reverse Proxy capabilities. """ @@ -597,7 +596,6 @@ def handle_request(self, request: HttpParser) -> None: }))) return - # print('headers', request.headers) logger.debug('<<< %s 0x%x %s', threading.get_ident(), id(self.model), request.body.decode('utf8')) response = None diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 34038990c..6028c07ac 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -45,7 +45,6 @@ evm_loader_id = os.environ.get("EVM_LOADER") COLLATERAL_POOL_BASE = os.environ.get("COLLATERAL_POOL_BASE") NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) -#evm_loader_id = "EfyDoGDRPy7wrLfSLyXrbhiAG6NmufMk1ytap13gLy1" location_bin = ".deploy_contract.bin" confirmation_check_delay = float(os.environ.get("NEON_CONFIRMATION_CHECK_DELAY", "0.1")) neon_cli_timeout = float(os.environ.get("NEON_CLI_TIMEOUT", "0.1")) @@ -66,7 +65,7 @@ os.environ.get("ETH_TOKEN_MINT", "HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU") ) -STORAGE_SIZE = 128*1024 +STORAGE_SIZE = 128 * 1024 ACCOUNT_INFO_LAYOUT = cStruct( "type" / Int8ul, @@ -106,6 +105,7 @@ def __init__(self, caller_token, eth_accounts): self.caller_token = caller_token self.eth_accounts = eth_accounts + class AccountInfo(NamedTuple): ether: eth_keys.PublicKey trx_count: int @@ -116,6 +116,7 @@ def frombytes(data): cont = ACCOUNT_INFO_LAYOUT.parse(data) return AccountInfo(cont.ether, cont.trx_count, PublicKey(cont.code_account)) + def create_account_layout(lamports, space, ether, nonce): return bytes.fromhex("02000000")+CREATE_ACCOUNT_LAYOUT.build(dict( lamports=lamports, @@ -130,12 +131,13 @@ def write_layout(offset, data): len(data).to_bytes(8, byteorder="little")+ data) + def accountWithSeed(base, seed, program): - # logger.debug(type(base), str(base), type(seed), str(seed), type(program), str(program)) result = PublicKey(sha256(bytes(base) + bytes(seed) + bytes(program)).digest()) logger.debug('accountWithSeed %s', str(result)) return result + def createAccountWithSeedTrx(funding, base, seed, lamports, space, program): seed_str = str(seed, 'utf8') data = SYSTEM_INSTRUCTIONS_LAYOUT.build( @@ -270,13 +272,14 @@ def call(self, *args): "--url", solana_url, "--evm_loader={}".format(evm_loader_id), ] + list(args) - print(cmd) + logger.debug(cmd) return subprocess.check_output(cmd, timeout=neon_cli_timeout, universal_newlines=True) except subprocess.CalledProcessError as err: import sys logger.debug("ERR: neon-cli error {}".format(err)) raise + def confirm_transaction(client, tx_sig, confirmations=0): """Confirm a transaction.""" TIMEOUT = 30 # 30 seconds pylint: disable=invalid-name @@ -289,18 +292,17 @@ def confirm_transaction(client, tx_sig, confirmations=0): status = resp['result']['value'][0] if status and (status['confirmationStatus'] == 'finalized' or \ status['confirmationStatus'] == 'confirmed' and status['confirmations'] >= confirmations): -# logger.debug('Confirmed transaction:', resp) return time.sleep(confirmation_check_delay) elapsed_time += confirmation_check_delay - #if not resp["result"]: raise RuntimeError("could not confirm transaction: ", tx_sig) - #return resp + def solana2ether(public_key): from web3 import Web3 return bytes(Web3.keccak(bytes.fromhex(public_key))[-20:]) + def ether2program(ether, program_id, base): if isinstance(ether, str): if ether.startswith('0x'): @@ -374,6 +376,7 @@ def call_emulated(contract_id, caller_id, data=None, value=None): raise Exception("evm emulator error ", result) return result + def extract_measurements_from_receipt(receipt): log_messages = receipt['result']['meta']['logMessages'] transaction = receipt['result']['transaction'] @@ -435,17 +438,20 @@ def get_measurements(result): logger.error("Can't get measurements %s"%err) logger.info("Failed result: %s"%json.dumps(result, indent=3)) + def send_transaction(client, trx, signer): result = client.send_transaction(trx, signer, opts=TxOpts(skip_confirmation=True, preflight_commitment=Confirmed)) confirm_transaction(client, result["result"]) result = client.get_confirmed_transaction(result["result"]) return result + def send_measured_transaction(client, trx, signer): result = send_transaction(client, trx, signer) get_measurements(result) return result + def check_if_program_exceeded_instructions(err_result): err_instruction = "Program failed to complete: exceeded maximum number of instructions allowed" err_budget = "failed: Computational budget exceeded" @@ -917,7 +923,7 @@ def create_account_list_by_emulate(signer, client, ethTrx): if address == sender_ether and NEW_USER_AIRDROP_AMOUNT > 0: trx.add(transfer2(Transfer2Params( - amount=NEW_USER_AIRDROP_AMOUNT*1_000_000_000, + amount=NEW_USER_AIRDROP_AMOUNT * GWEI_PER_ETH_COUNT, decimals=9, dest=get_associated_token_address(PublicKey(acc_desc["account"]), ETH_TOKEN_MINT_ID), mint=ETH_TOKEN_MINT_ID, @@ -1084,8 +1090,10 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste def createEtherAccountTrx(client, ether, evm_loader_id, signer, code_acc=None): if isinstance(ether, str): - if ether.startswith('0x'): ether = ether[2:] - else: ether = ether.hex() + if ether.startswith('0x'): + ether = ether[2:] + else: + ether = ether.hex() (sol, nonce) = ether2program(ether, evm_loader_id, signer.public_key()) associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) @@ -1126,7 +1134,8 @@ def createEtherAccountTrx(client, ether, evm_loader_id, signer, code_acc=None): AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), ])) - return (trx, sol, associated_token) + return trx, sol, associated_token + def createERC20TokenAccountTrx(signer, token_info): trx = Transaction() @@ -1147,7 +1156,6 @@ def createERC20TokenAccountTrx(signer, token_info): return trx - def write_trx_to_holder_account(signer, client, holder, ethTrx): msg = ethTrx.signature() + len(ethTrx.unsigned_msg()).to_bytes(8, byteorder="little") + ethTrx.unsigned_msg() @@ -1184,11 +1192,13 @@ def _getAccountData(client, account, expected_length, owner=None): raise Exception("Wrong data length for account data {}".format(account)) return data + def getAccountInfo(client, eth_acc, base_account): (account_sol, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader_id, base_account) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) + def getLamports(client, evm_loader, eth_acc, base_account): (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) return int(client.get_balance(account, commitment=Confirmed)['result']['value']) @@ -1210,11 +1220,12 @@ def getTokens(client, signer, evm_loader, eth_acc, base_account): def getTokenAddr(account): return get_associated_token_address(PublicKey(account), ETH_TOKEN_MINT_ID) + def make_instruction_data_from_tx(instruction, private_key=None): if isinstance(instruction, dict): - if instruction['chainId'] == None: + if instruction['chainId'] is None: raise Exception("chainId value is needed in input dict") - if private_key == None: + if private_key is None: raise Exception("Needed private key for transaction creation from fields") signed_tx = w3.eth.account.sign_transaction(instruction, private_key) From 505a58a713bb92297dbec765e9c02fb37fd7ed76 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 15:56:47 +0500 Subject: [PATCH 04/95] rename calling getTokens function --- proxy/plugin/solana_rest_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 45f328e72..27eacebff 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -25,7 +25,7 @@ import base58 import traceback import threading -from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, getTokens, \ +from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, get_token_account_balance, \ getAccountInfo, solana_cli, call_signed, solana_url, call_emulated, \ Trx, EthereumError, create_collateral_pool_address, getTokenAddr, STORAGE_SIZE, neon_config_load from solana.rpc.commitment import Commitment, Confirmed @@ -150,7 +150,7 @@ def eth_getBalance(self, account, tag): """ eth_acc = EthereumAddress(account) logger.debug('eth_getBalance: %s %s', account, eth_acc) - balance = getTokens(self.client, self.signer, evm_loader_id, eth_acc, self.signer.public_key()) + balance = get_token_account_balance(self.client, self.signer, evm_loader_id, eth_acc, self.signer.public_key()) return hex(balance*10**9) From 1626d5b214803fa5b9651947d054f60c4a00c1a4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 16:02:13 +0500 Subject: [PATCH 05/95] Meet SolanaErrors --- proxy/plugin/solana_rest_api_tools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 6028c07ac..18e9f3642 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -67,6 +67,11 @@ STORAGE_SIZE = 128 * 1024 + +class SolanaErrors(Enum): + AccountNotFound = "Invalid param: could not find account" + + ACCOUNT_INFO_LAYOUT = cStruct( "type" / Int8ul, "ether" / Bytes(20), From 0226e970cdd25422e956d2527af00170bcc73ede Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 16:05:08 +0500 Subject: [PATCH 06/95] Implement creating account on getting balance --- proxy/plugin/solana_rest_api.py | 4 +- proxy/plugin/solana_rest_api_tools.py | 73 ++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 27eacebff..209d332f9 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -25,7 +25,7 @@ import base58 import traceback import threading -from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, get_token_account_balance, \ +from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, get_token_balance, \ getAccountInfo, solana_cli, call_signed, solana_url, call_emulated, \ Trx, EthereumError, create_collateral_pool_address, getTokenAddr, STORAGE_SIZE, neon_config_load from solana.rpc.commitment import Commitment, Confirmed @@ -150,7 +150,7 @@ def eth_getBalance(self, account, tag): """ eth_acc = EthereumAddress(account) logger.debug('eth_getBalance: %s %s', account, eth_acc) - balance = get_token_account_balance(self.client, self.signer, evm_loader_id, eth_acc, self.signer.public_key()) + balance = get_token_balance(self.client, self.signer, evm_loader_id, eth_acc, self.signer.public_key()) return hex(balance*10**9) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 18e9f3642..351b111f4 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1104,7 +1104,8 @@ def createEtherAccountTrx(client, ether, evm_loader_id, signer, code_acc=None): logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) logger.debug('associatedTokenAccount: {}'.format(associated_token)) base = signer.public_key() - data=bytes.fromhex('02000000')+CREATE_ACCOUNT_LAYOUT.build(dict( + + data = bytes.fromhex('02000000')+CREATE_ACCOUNT_LAYOUT.build(dict( lamports=0, space=0, ether=bytes.fromhex(ether), @@ -1208,19 +1209,67 @@ def getLamports(client, evm_loader, eth_acc, base_account): (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def getTokens(client, signer, evm_loader, eth_acc, base_account): - (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) - token_account = get_associated_token_address(PublicKey(account), ETH_TOKEN_MINT_ID) - balance = client.get_token_account_balance(token_account, commitment=Confirmed) - if 'error' in balance: - if NEW_USER_AIRDROP_AMOUNT > 0: - return NEW_USER_AIRDROP_AMOUNT * 1_000_000_000 - else: - logger.debug("'error' in balance:") - return 0 +def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, token_account: str, eth_acc: EthereumAddress): + logger.debug(f"Create and mint on account: {eth_acc} aka: {token_account} at token: {ETH_TOKEN_MINT_ID}") + trx = Transaction() + sender_sol_info = client.get_account_info(token_account, commitment=Confirmed) + value = get_from_dict(sender_sol_info, "result", "value") + if value is not None: + logger.warning(f"Failed to create eth account: {eth_acc}, already exists") + return + + transfer_instruction = createEtherAccountTrx(client, bytes(eth_acc).hex(), evm_loader_id, signer) + trx.add(transfer_instruction[0]) + + transfer_instruction = transfer2(Transfer2Params(source=getTokenAddr(signer.public_key()), + owner=signer.public_key(), + dest=getTokenAddr(token_account), + amount=NEW_USER_AIRDROP_AMOUNT * GWEI_PER_ETH_COUNT, + decimals=9, + mint=ETH_TOKEN_MINT_ID, + program_id=TOKEN_PROGRAM_ID)) + trx.add(transfer_instruction) + + logger.debug(f"Token transfer to: {token_account} as ethereum {eth_acc} amount {NEW_USER_AIRDROP_AMOUNT}") + result = send_transaction(client, trx, signer) + error = result.get("error") + if error is not None: + logger.error(f"Failed to create and mint token account: {eth_acc}, error occurred: {error}") + raise Exception("Create account error") + + +def get_token_balance_impl(client: SolanaClient, account: str, eth_acc: EthereumAddress) -> [Optional[int], Optional[Union[Dict, str]]]: + token_account = get_associated_token_address(PublicKey(account), ETH_TOKEN_MINT_ID) + rpc_response = client.get_token_account_balance(token_account, commitment=Confirmed) + error = rpc_response.get('error') + if error is None: + balance = get_from_dict(rpc_response, "result", "value", "amount") + if balance is None: + return None, f"Failed to get token balance from: {rpc_response}, by eth account:" \ + f" {eth_acc} aka: {token_account} at token: {ETH_TOKEN_MINT_ID}" + return balance, None + return None, error + + +def get_token_balance(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress, base_account: PublicKey) -> int: + account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) + logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") + + balance, error = get_token_balance_impl(client, account, eth_acc) + if error is None: + return int(balance) + + if error.get("message") == SolanaErrors.AccountNotFound.value and NEW_USER_AIRDROP_AMOUNT > 0: + logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") + create_and_mint_tkn_acc(client, signer, account, eth_acc) + balance, error = get_token_balance_impl(client, account, eth_acc) + if error is None: + return int(balance) + + logger.error(f"Failed to get balance for account: {eth_acc}, error occurred: {error}") + raise Exception("Getting balance error") - return int(balance['result']['value']['amount']) def getTokenAddr(account): return get_associated_token_address(PublicKey(account), ETH_TOKEN_MINT_ID) From 6451aa63e1df39515db0951e153c5dc1d2b9808a Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 2 Nov 2021 20:44:05 +0500 Subject: [PATCH 07/95] Move tests and add get_from_dict test --- proxy/{ => testing}/test_cancel_hanged.py | 0 .../{ => testing}/test_erc20_wrapper_contract.py | 0 proxy/{ => testing}/test_eth_getBlockByNumber.py | 0 proxy/{ => testing}/test_eth_getLogs.py | 0 .../{ => testing}/test_eth_sendRawTransaction.py | 2 +- .../{ => testing}/test_resize_storage_account.py | 0 proxy/{ => testing}/test_user_stories.py | 0 proxy/testing/test_utils.py | 16 ++++++++++++++++ proxy/{ => testing}/test_web3_clientVersion.py | 0 9 files changed, 17 insertions(+), 1 deletion(-) rename proxy/{ => testing}/test_cancel_hanged.py (100%) rename proxy/{ => testing}/test_erc20_wrapper_contract.py (100%) rename proxy/{ => testing}/test_eth_getBlockByNumber.py (100%) rename proxy/{ => testing}/test_eth_getLogs.py (100%) rename proxy/{ => testing}/test_eth_sendRawTransaction.py (99%) rename proxy/{ => testing}/test_resize_storage_account.py (100%) rename proxy/{ => testing}/test_user_stories.py (100%) create mode 100644 proxy/testing/test_utils.py rename proxy/{ => testing}/test_web3_clientVersion.py (100%) diff --git a/proxy/test_cancel_hanged.py b/proxy/testing/test_cancel_hanged.py similarity index 100% rename from proxy/test_cancel_hanged.py rename to proxy/testing/test_cancel_hanged.py diff --git a/proxy/test_erc20_wrapper_contract.py b/proxy/testing/test_erc20_wrapper_contract.py similarity index 100% rename from proxy/test_erc20_wrapper_contract.py rename to proxy/testing/test_erc20_wrapper_contract.py diff --git a/proxy/test_eth_getBlockByNumber.py b/proxy/testing/test_eth_getBlockByNumber.py similarity index 100% rename from proxy/test_eth_getBlockByNumber.py rename to proxy/testing/test_eth_getBlockByNumber.py diff --git a/proxy/test_eth_getLogs.py b/proxy/testing/test_eth_getLogs.py similarity index 100% rename from proxy/test_eth_getLogs.py rename to proxy/testing/test_eth_getLogs.py diff --git a/proxy/test_eth_sendRawTransaction.py b/proxy/testing/test_eth_sendRawTransaction.py similarity index 99% rename from proxy/test_eth_sendRawTransaction.py rename to proxy/testing/test_eth_sendRawTransaction.py index 62a035e39..538ed55cc 100644 --- a/proxy/test_eth_sendRawTransaction.py +++ b/proxy/testing/test_eth_sendRawTransaction.py @@ -2,7 +2,7 @@ import os from web3 import Web3 from solcx import install_solc -from .common.constants import GWEI_PER_ETH_COUNT +from ..common.constants import GWEI_PER_ETH_COUNT # install_solc(version='latest') install_solc(version='0.7.0') diff --git a/proxy/test_resize_storage_account.py b/proxy/testing/test_resize_storage_account.py similarity index 100% rename from proxy/test_resize_storage_account.py rename to proxy/testing/test_resize_storage_account.py diff --git a/proxy/test_user_stories.py b/proxy/testing/test_user_stories.py similarity index 100% rename from proxy/test_user_stories.py rename to proxy/testing/test_user_stories.py diff --git a/proxy/testing/test_utils.py b/proxy/testing/test_utils.py new file mode 100644 index 000000000..4d09a3ee7 --- /dev/null +++ b/proxy/testing/test_utils.py @@ -0,0 +1,16 @@ +import unittest +from ..common.utils import get_from_dict + + +class TestUtils(unittest.TestCase): + + def test_get_from_dict(self): + test_dict = {"a": {"b": {"c": 1}}} + + self.assertEqual(1, get_from_dict(test_dict, "a", "b", "c")) + self.assertEqual({"b": {"c": 1}}, get_from_dict(test_dict, "a")) + + self.assertIsNone(get_from_dict(test_dict, "b", "c", "a")) + self.assertIsNone(get_from_dict(None, "a")) + self.assertIsNone(get_from_dict(555, "a")) + self.assertIsNone(get_from_dict({}, "a")) diff --git a/proxy/test_web3_clientVersion.py b/proxy/testing/test_web3_clientVersion.py similarity index 100% rename from proxy/test_web3_clientVersion.py rename to proxy/testing/test_web3_clientVersion.py From a9a9c25a6cdf13890604ad68945ef39c7aeb44e7 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 4 Nov 2021 10:22:18 +0500 Subject: [PATCH 08/95] Add test "Metamask creates an account" --- proxy/testing/test_establish_eth_accounts.py | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 proxy/testing/test_establish_eth_accounts.py diff --git a/proxy/testing/test_establish_eth_accounts.py b/proxy/testing/test_establish_eth_accounts.py new file mode 100644 index 000000000..efde8c00d --- /dev/null +++ b/proxy/testing/test_establish_eth_accounts.py @@ -0,0 +1,23 @@ +import unittest +import eth_account +import eth_typing +import eth_utils +import os +from web3 import Web3 + +NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) + + +class TestEstablishEthAccounts(unittest.TestCase): + + def setUp(self) -> None: + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + self.proxy = Web3(Web3.HTTPProvider(proxy_url)) + + def test_metamask_creates_account(self): + + account: eth_account.account.LocalAccount = eth_account.account.Account.create() + block_number: eth_typing.BlockNumber = self.proxy.eth.get_block_number() + balance_wei = self.proxy.eth.get_balance(account.address, block_identifier=block_number) + expected_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') + self.assertEqual(expected_wei, balance_wei) From 644f28d5c02e78365230c86a442c0bc3c682c0d9 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 8 Nov 2021 00:56:01 +0500 Subject: [PATCH 09/95] Just not ot loose changes --- proxy/common/data.py | 6 ++ proxy/plugin/solana_rest_api_tools.py | 86 ++++++++++--------- .../testing/test_airdropping_eth_accounts.py | 69 +++++++++++++++ proxy/testing/test_erc20_wrapper_contract.py | 6 +- proxy/testing/test_establish_eth_accounts.py | 23 ----- 5 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 proxy/common/data.py create mode 100644 proxy/testing/test_airdropping_eth_accounts.py delete mode 100644 proxy/testing/test_establish_eth_accounts.py diff --git a/proxy/common/data.py b/proxy/common/data.py new file mode 100644 index 000000000..ce90b3b18 --- /dev/null +++ b/proxy/common/data.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass + +@dataclass +class TokenAccountInfo: + eth_account: str + associated_account: diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 2d51c4726..4081a72de 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -10,8 +10,10 @@ import time from datetime import datetime from hashlib import sha256 -from typing import NamedTuple, Optional, Union, Dict +from typing import NamedTuple, Optional, Union, Dict, Tuple from enum import Enum + +import eth_utils import rlp from base58 import b58decode, b58encode from construct import Bytes, Int8ul, Int32ul, Int64ul @@ -131,6 +133,7 @@ def create_account_layout(lamports, space, ether, nonce): nonce=nonce )) + def write_holder_layout(nonce, offset, data): return (bytes.fromhex('12')+ nonce.to_bytes(8, byteorder='little')+ @@ -279,7 +282,7 @@ def call(self, *args): "--url", solana_url, "--evm_loader={}".format(evm_loader_id), ] + list(args) - logger.debug(cmd) + logger.debug(f"Calling: \"{' '.join(cmd)}\"") return subprocess.check_output(cmd, timeout=neon_cli_timeout, universal_newlines=True) except subprocess.CalledProcessError as err: import sys @@ -312,8 +315,7 @@ def solana2ether(public_key): def ether2program(ether, program_id, base): if isinstance(ether, str): - if ether.startswith('0x'): - ether = ether[2:] + pass else: ether = ether.hex() output = neon_cli().call("create-program-address", ether) @@ -845,6 +847,7 @@ def simulate_continue(signer, client, perm_accs, trx_accs, step_count): logger.debug("tx_count = {}, step_count = {}".format(continue_count, step_count)) return (continue_count, step_count) + def create_account_list_by_emulate(signer, client, ethTrx): sender_ether = bytes.fromhex(ethTrx.sender()) add_keys_05 = [] @@ -865,7 +868,6 @@ def create_account_list_by_emulate(signer, client, ethTrx): for acc_desc in output_json["accounts"]: if acc_desc["new"] == False: - address = bytes.fromhex(acc_desc["address"][2:]) if acc_desc["code_size_current"] and acc_desc["code_size"]: if acc_desc["code_size"] > acc_desc["code_size_current"]: code_size = acc_desc["code_size"] + 2048 @@ -873,7 +875,7 @@ def create_account_list_by_emulate(signer, client, ethTrx): code_account_new = accountWithSeed(signer.public_key(), seed, PublicKey(evm_loader_id)) logger.debug("creating new code_account with increased size %s", code_account_new) - create_account_with_seed(client, signer, signer, seed, code_size); + create_account_with_seed(client, signer, signer, seed, code_size) logger.debug("resized account is created %s", code_account_new) resize_instr.append(TransactionInstruction( @@ -906,7 +908,6 @@ def create_account_list_by_emulate(signer, client, ethTrx): success = True logger.debug("successful code and storage migration, %s", instr.keys[0].pubkey) break - # wait for unlock account time.sleep(1) count = count+1 @@ -927,26 +928,13 @@ def create_account_list_by_emulate(signer, client, ethTrx): code_size = acc_desc["code_size"] + 2048 code_account_balance = client.get_minimum_balance_for_rent_exemption(code_size)["result"] trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) - # add_keys_05.append(AccountMeta(pubkey=code_account, is_signer=False, is_writable=acc_desc["writable"])) code_account_writable = acc_desc["writable"] - (create_trx, solana_address, token_address) = createEtherAccountTrx(client, address, evm_loader_id, signer, code_account) + create_trx, solana_address, token_address = create_eth_account_trx(client, address, evm_loader_id, signer, code_account) trx.add(create_trx) + add_airdrop_transfer_to_trx(signer, solana_address, trx) + - if address == sender_ether and NEW_USER_AIRDROP_AMOUNT > 0: - trx.add(transfer2(Transfer2Params( - amount=NEW_USER_AIRDROP_AMOUNT * GWEI_PER_ETH_COUNT, - decimals=9, - dest=get_associated_token_address(PublicKey(acc_desc["account"]), ETH_TOKEN_MINT_ID), - mint=ETH_TOKEN_MINT_ID, - owner=signer.public_key(), - program_id=TOKEN_PROGRAM_ID, - source=getTokenAddr(signer.public_key()), - ))) - logger.debug("Token transfer to %s as ethereum 0x%s amount %s", - get_associated_token_address(PublicKey(acc_desc["account"]), ETH_TOKEN_MINT_ID), - acc_desc["address"], - str(NEW_USER_AIRDROP_AMOUNT)) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -974,7 +962,6 @@ def create_account_list_by_emulate(signer, client, ethTrx): if acc_desc["contract"]: add_keys_05.append(AccountMeta(pubkey=acc_desc["contract"], is_signer=False, is_writable=acc_desc["writable"])) - for token_account in output_json["token_accounts"]: add_keys_05.append(AccountMeta(pubkey=PublicKey(token_account["key"]), is_signer=False, is_writable=True)) @@ -1100,16 +1087,24 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste return call_continue(signer, client, perm_accs, trx_accs, steps) -def createEtherAccountTrx(client, ether, evm_loader_id, signer, code_acc=None): +def create_eth_account_trx(client: SolanaClient, ether: EthereumAddress, evm_loader_id, signer, code_acc=None) -> Tuple[Transaction, str, str]: if isinstance(ether, str): if ether.startswith('0x'): ether = ether[2:] else: ether = ether.hex() + logger.debug(f"Signer: {signer}") (sol, nonce) = ether2program(ether, evm_loader_id, signer.public_key()) associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) logger.debug('associatedTokenAccount: {}'.format(associated_token)) + + # sender_sol_info = client.get_account_info(sol, commitment=Confirmed) + # value = get_from_dict(sender_sol_info, "result", "value") + # if value is not None: + # logger.error(f"Failed to create eth account: {ether}, associated: {associated_token}, already exists") + # raise Exception("Account already exists") + base = signer.public_key() data = bytes.fromhex('02000000')+CREATE_ACCOUNT_LAYOUT.build(dict( @@ -1218,28 +1213,35 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, token_account: str, eth_acc: EthereumAddress): - logger.debug(f"Create and mint on account: {eth_acc} aka: {token_account} at token: {ETH_TOKEN_MINT_ID}") - trx = Transaction() - sender_sol_info = client.get_account_info(token_account, commitment=Confirmed) - value = get_from_dict(sender_sol_info, "result", "value") - if value is not None: - logger.warning(f"Failed to create eth account: {eth_acc}, already exists") - return - - create_trx = createEtherAccountTrx(client, bytes(eth_acc).hex(), evm_loader_id, signer) - trx.add(create_trx[0]) - - transfer_instruction = transfer2(Transfer2Params(source=getTokenAddr(signer.public_key()), - owner=signer.public_key(), - dest=getTokenAddr(token_account), +def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, trx: Transaction): + dest_token_account = getTokenAddr(dest_account) + owner_addr = owner_account.public_key() + owner_token_addr = getTokenAddr(owner_addr) + transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, + owner=owner_addr, + dest=dest_token_account, amount=NEW_USER_AIRDROP_AMOUNT * GWEI_PER_ETH_COUNT, decimals=9, mint=ETH_TOKEN_MINT_ID, program_id=TOKEN_PROGRAM_ID)) trx.add(transfer_instruction) + # logger.debug("Token transfer from: to %s as ethereum 0x%s amount %s", + # get_associated_token_address(PublicKey(acc_desc["account"]), ETH_TOKEN_MINT_ID), + # acc_desc["address"], + # str(NEW_USER_AIRDROP_AMOUNT)) + - logger.debug(f"Token transfer to: {token_account} as ethereum {eth_acc} amount {NEW_USER_AIRDROP_AMOUNT}") +def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: str, trx: Transaction): + create_trx, solana_address, token_address = create_eth_account_trx(client, eth_acc, evm_loader_id, signer) + logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}, at token: {ETH_TOKEN_MINT_ID}") + trx.add(create_trx) + add_airdrop_transfer_to_trx(signer, solana_address, trx) + logger.debug(f"Token transfer from: {signer.public_key()}, to eth account: {eth_acc}, aka: {solana_address}, token_address: {token_address}, value: {NEW_USER_AIRDROP_AMOUNT}") + + +def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): + trx = Transaction() + create_and_mint_token_trx(client, signer, str(eth_acc), trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: @@ -1270,7 +1272,7 @@ def get_token_balance(client: SolanaClient, signer: SolanaAccount, evm_loader: s if error.get("message") == SolanaErrors.AccountNotFound.value and NEW_USER_AIRDROP_AMOUNT > 0: logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - create_and_mint_tkn_acc(client, signer, account, eth_acc) + create_and_mint_tkn_acc(client, signer, eth_acc) balance, error = get_token_balance_impl(client, account, eth_acc) if error is None: return int(balance) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py new file mode 100644 index 000000000..f36ffb92f --- /dev/null +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -0,0 +1,69 @@ +import unittest +import eth_account +import eth_typing +import eth_utils +import os +from web3 import Web3 +import solcx +import logging + +from semantic_version import Version + +NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) + + +class TestAirdroppingEthAccounts(unittest.TestCase): + + @classmethod + def setUpClass(cls) -> None: + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + cls.proxy = Web3(Web3.HTTPProvider(proxy_url)) + cls.logger = logging.getLogger() + cls.logger.setLevel(logging.DEBUG) + + def test_airdrop_on_get_balance(self): + account: eth_account.account.LocalAccount = eth_account.account.Account.create() + block_number: eth_typing.BlockNumber = self.proxy.eth.get_block_number() + actual_balance_wei = self.proxy.eth.get_balance(account.address, block_identifier=block_number) + expected_balance_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') + self.assertEqual(expected_balance_wei, actual_balance_wei) + + def test_airdrop_on_deploy(self): + contract_owner: eth_account.account.LocalAccount = self.proxy.eth.account.create() + print(f"contract_owner {contract_owner.address}") + + compiled_sol = solcx.compile_source(self._CONTRACT_STORAGE_SOURCE) + contract_id, contract_interface = compiled_sol.popitem() + storage = self.proxy.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + nonce = self.proxy.eth.get_transaction_count(contract_owner.address) + chain_id = self.proxy.eth.chain_id + trx_signed = self.proxy.eth.account.sign_transaction( + dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=0, to='', value=0, data=storage.bytecode), + contract_owner.key + ) + trx_hash = self.proxy.eth.send_raw_transaction(trx_signed.rawTransaction) + trx_receipt = self.proxy.eth.wait_for_transaction_receipt(trx_hash) + storage_contract = self.proxy.eth.contract( + address=trx_receipt.contractAddress, + abi=storage.abi + ) + actual_balance_wei = self.proxy.eth.get_balance(storage_contract.address, block_identifier="latest") + expected_balance_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') + + owner_balance = self.proxy.eth.get_balance(contract_owner.address, block_identifier="latest") + + self.assertEqual(expected_balance_wei, owner_balance) + self.assertEqual(expected_balance_wei, actual_balance_wei) + + _CONTRACT_STORAGE_SOURCE = '''pragma solidity >=0.7.0 <0.9.0; + contract Storage { + uint256 number; + function store(uint256 num) public { + number = num; + } + function retrieve() public view returns (uint256) { + return number; + } + } + ''' + diff --git a/proxy/testing/test_erc20_wrapper_contract.py b/proxy/testing/test_erc20_wrapper_contract.py index de8b61ba5..2b05c34f2 100644 --- a/proxy/testing/test_erc20_wrapper_contract.py +++ b/proxy/testing/test_erc20_wrapper_contract.py @@ -15,7 +15,7 @@ from solana.account import Account as SolanaAccount from solana.publickey import PublicKey -from proxy.plugin.solana_rest_api_tools import createERC20TokenAccountTrx, createEtherAccountTrx +from proxy.plugin.solana_rest_api_tools import createERC20TokenAccountTrx, create_eth_account_trx # install_solc(version='latest') install_solc(version='0.7.6') @@ -66,7 +66,7 @@ event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); - + function approveSolana(bytes32 spender, uint64 value) external returns (bool); event ApprovalSolana(address indexed owner, bytes32 indexed spender, uint64 value); } @@ -147,7 +147,7 @@ def deploy_erc20_wrapper_contract(self): compiled_wrapper = compile_source(ERC20_WRAPPER_SOURCE) wrapper_id, wrapper_interface = compiled_wrapper.popitem() self.wrapper = wrapper_interface - + erc20 = proxy.eth.contract(abi=self.wrapper['abi'], bytecode=wrapper_interface['bin']) nonce = proxy.eth.get_transaction_count(proxy.eth.default_account) tx = {'nonce': nonce} diff --git a/proxy/testing/test_establish_eth_accounts.py b/proxy/testing/test_establish_eth_accounts.py deleted file mode 100644 index efde8c00d..000000000 --- a/proxy/testing/test_establish_eth_accounts.py +++ /dev/null @@ -1,23 +0,0 @@ -import unittest -import eth_account -import eth_typing -import eth_utils -import os -from web3 import Web3 - -NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) - - -class TestEstablishEthAccounts(unittest.TestCase): - - def setUp(self) -> None: - proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - self.proxy = Web3(Web3.HTTPProvider(proxy_url)) - - def test_metamask_creates_account(self): - - account: eth_account.account.LocalAccount = eth_account.account.Account.create() - block_number: eth_typing.BlockNumber = self.proxy.eth.get_block_number() - balance_wei = self.proxy.eth.get_balance(account.address, block_identifier=block_number) - expected_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') - self.assertEqual(expected_wei, balance_wei) From 3bc25efc91b42f4a51a24531e47a87e804303178 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 8 Nov 2021 02:05:28 +0500 Subject: [PATCH 10/95] implemented --- proxy/plugin/solana_rest_api_tools.py | 35 ++++++++----------- .../testing/test_airdropping_eth_accounts.py | 2 -- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 4081a72de..dc90ec110 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -316,6 +316,8 @@ def solana2ether(public_key): def ether2program(ether, program_id, base): if isinstance(ether, str): pass + elif isinstance(ether, EthereumAddress): + ether = str(ether) else: ether = ether.hex() output = neon_cli().call("create-program-address", ether) @@ -930,11 +932,7 @@ def create_account_list_by_emulate(signer, client, ethTrx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - create_trx, solana_address, token_address = create_eth_account_trx(client, address, evm_loader_id, signer, code_account) - trx.add(create_trx) - add_airdrop_transfer_to_trx(signer, solana_address, trx) - - + create_and_mint_token_trx(client, signer, EthereumAddress(address), trx, code_account) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1087,30 +1085,26 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste return call_continue(signer, client, perm_accs, trx_accs, steps) -def create_eth_account_trx(client: SolanaClient, ether: EthereumAddress, evm_loader_id, signer, code_acc=None) -> Tuple[Transaction, str, str]: - if isinstance(ether, str): - if ether.startswith('0x'): - ether = ether[2:] - else: - ether = ether.hex() +def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, str, str]: + logger.debug(f"Signer: {signer}") (sol, nonce) = ether2program(ether, evm_loader_id, signer.public_key()) associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) logger.debug('associatedTokenAccount: {}'.format(associated_token)) - # sender_sol_info = client.get_account_info(sol, commitment=Confirmed) - # value = get_from_dict(sender_sol_info, "result", "value") - # if value is not None: - # logger.error(f"Failed to create eth account: {ether}, associated: {associated_token}, already exists") - # raise Exception("Account already exists") + sender_sol_info = client.get_account_info(sol, commitment=Confirmed) + value = get_from_dict(sender_sol_info, "result", "value") + if value is not None: + logger.error(f"Failed to create eth account: {ether}, associated: {associated_token}, already exists") + raise Exception("Account already exists") base = signer.public_key() data = bytes.fromhex('02000000')+CREATE_ACCOUNT_LAYOUT.build(dict( lamports=0, space=0, - ether=bytes.fromhex(ether), + ether=bytes(ether), nonce=nonce)) trx = Transaction() if code_acc is None: @@ -1231,8 +1225,8 @@ def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, # str(NEW_USER_AIRDROP_AMOUNT)) -def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: str, trx: Transaction): - create_trx, solana_address, token_address = create_eth_account_trx(client, eth_acc, evm_loader_id, signer) +def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: str, trx: Transaction, code_acc=None): + create_trx, solana_address, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}, at token: {ETH_TOKEN_MINT_ID}") trx.add(create_trx) add_airdrop_transfer_to_trx(signer, solana_address, trx) @@ -1241,7 +1235,7 @@ def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_a def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): trx = Transaction() - create_and_mint_token_trx(client, signer, str(eth_acc), trx) + create_and_mint_token_trx(client, signer, eth_acc, trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: @@ -1263,6 +1257,7 @@ def get_token_balance_impl(client: SolanaClient, account: str, eth_acc: Ethereum def get_token_balance(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress, base_account: PublicKey) -> int: + account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index f36ffb92f..1c942abc0 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -7,8 +7,6 @@ import solcx import logging -from semantic_version import Version - NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) From db81aa3a32f075c7e65f1b47006757f89d024a13 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 8 Nov 2021 02:33:17 +0500 Subject: [PATCH 11/95] Fix tests on CI --- .buildkite/steps/deploy-test.sh | 1 + proxy/testing/test_airdropping_eth_accounts.py | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.buildkite/steps/deploy-test.sh b/.buildkite/steps/deploy-test.sh index 9ffd4f3e1..477a8998e 100755 --- a/.buildkite/steps/deploy-test.sh +++ b/.buildkite/steps/deploy-test.sh @@ -69,6 +69,7 @@ docker run --rm -ti --network=container:proxy \ -e EVM_LOADER \ -e SOLANA_URL \ -e EXTRA_GAS=100000 \ + -e NEW_USER_AIRDROP_AMOUNT= 100 \ --entrypoint ./proxy/deploy-test.sh \ ${EXTRA_ARGS:-} \ $PROXY_IMAGE \ diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 1c942abc0..3b3e76f9e 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -16,8 +16,6 @@ class TestAirdroppingEthAccounts(unittest.TestCase): def setUpClass(cls) -> None: proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') cls.proxy = Web3(Web3.HTTPProvider(proxy_url)) - cls.logger = logging.getLogger() - cls.logger.setLevel(logging.DEBUG) def test_airdrop_on_get_balance(self): account: eth_account.account.LocalAccount = eth_account.account.Account.create() @@ -28,8 +26,6 @@ def test_airdrop_on_get_balance(self): def test_airdrop_on_deploy(self): contract_owner: eth_account.account.LocalAccount = self.proxy.eth.account.create() - print(f"contract_owner {contract_owner.address}") - compiled_sol = solcx.compile_source(self._CONTRACT_STORAGE_SOURCE) contract_id, contract_interface = compiled_sol.popitem() storage = self.proxy.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) From 3c63dab1f9d00a8b67ddb88a4238c3336b356373 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 8 Nov 2021 02:38:59 +0500 Subject: [PATCH 12/95] set NEW_USER_AIRDROP_AMOUNT on CI --- .buildkite/steps/deploy-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/steps/deploy-test.sh b/.buildkite/steps/deploy-test.sh index 477a8998e..c48fd900b 100755 --- a/.buildkite/steps/deploy-test.sh +++ b/.buildkite/steps/deploy-test.sh @@ -69,7 +69,7 @@ docker run --rm -ti --network=container:proxy \ -e EVM_LOADER \ -e SOLANA_URL \ -e EXTRA_GAS=100000 \ - -e NEW_USER_AIRDROP_AMOUNT= 100 \ + -e NEW_USER_AIRDROP_AMOUNT=100 \ --entrypoint ./proxy/deploy-test.sh \ ${EXTRA_ARGS:-} \ $PROXY_IMAGE \ From 771ca644ddc7fc26133079fc92098239214f1262 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 8 Nov 2021 22:47:05 +0500 Subject: [PATCH 13/95] improve logging --- proxy/plugin/solana_rest_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 921dbb0ac..9efef920d 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -457,7 +457,10 @@ def eth_sendRawTransaction(self, rawTrx): return eth_signature except solana.rpc.api.SendTransactionError as err: + logs = err.result.get("data", {}).pop("logs", []) logger.debug("eth_sendRawTransaction solana.rpc.api.SendTransactionError:%s", err.result) + logs_str = '\n\t'.join(logs) + logger.debug(f"Logs: {logs_str} ") raise except EthereumError as err: logger.debug("eth_sendRawTransaction EthereumError:%s", err) From a90d8ad3ddf69b5dd4a64da2b4f5e1bfabd50c7f Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 9 Nov 2021 10:36:18 +0500 Subject: [PATCH 14/95] improve logging of SendTransactionError --- proxy/plugin/solana_rest_api.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 9efef920d..0620c4445 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -457,10 +457,8 @@ def eth_sendRawTransaction(self, rawTrx): return eth_signature except solana.rpc.api.SendTransactionError as err: - logs = err.result.get("data", {}).pop("logs", []) - logger.debug("eth_sendRawTransaction solana.rpc.api.SendTransactionError:%s", err.result) - logs_str = '\n\t'.join(logs) - logger.debug(f"Logs: {logs_str} ") + logs = "\n\t".join(err.result.get("data", {}).pop("logs", [])) + logger.debug(f"eth_sendRawTransaction solana.rpc.api.SendTransactionError: {err.result}, {logs}") raise except EthereumError as err: logger.debug("eth_sendRawTransaction EthereumError:%s", err) From aa2f329cd3ea564051fd377950a169c2a61f6b44 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 9 Nov 2021 15:48:26 +0500 Subject: [PATCH 15/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 29 +++++++++++---------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index dc90ec110..26530a8e9 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -13,18 +13,18 @@ from typing import NamedTuple, Optional, Union, Dict, Tuple from enum import Enum -import eth_utils import rlp from base58 import b58decode, b58encode from construct import Bytes, Int8ul, Int32ul, Int64ul from construct import Struct as cStruct from eth_keys import keys as eth_keys +import eth_utils + from sha3 import keccak_256 from web3.auto import w3 from solana.account import Account as SolanaAccount from solana.blockhash import Blockhash -from solana.publickey import PublicKey from solana.rpc.api import Client as SolanaClient, SendTransactionError from solana.rpc.commitment import Commitment, Confirmed from solana.rpc.types import TxOpts @@ -37,7 +37,6 @@ from spl.token.instructions import get_associated_token_address, create_associated_token_account, transfer2, Transfer2Params from ..common.utils import get_from_dict -from ..common.constants import GWEI_PER_ETH_COUNT from .eth_proto import Trx logger = logging.getLogger(__name__) @@ -322,7 +321,7 @@ def ether2program(ether, program_id, base): ether = ether.hex() output = neon_cli().call("create-program-address", ether) items = output.rstrip().split(' ') - return (items[0], int(items[1])) + return items[0], int(items[1]) def ether2seed(ether, program_id, base): @@ -332,7 +331,7 @@ def ether2seed(ether, program_id, base): seed = b58encode(bytes.fromhex(ether)) acc = accountWithSeed(base, seed, PublicKey(program_id)) logger.debug('ether2program: {} {} => {} (seed {})'.format(ether, 255, acc, seed)) - return (acc, 255, seed) + return acc, 255, seed def neon_config_load(ethereum_model): @@ -981,7 +980,7 @@ def create_account_list_by_emulate(signer, client, ethTrx): trx_accs = TransactionInfo(caller_token, eth_accounts, ethTrx.nonce) - return (trx_accs, sender_ether, trx) + return trx_accs, sender_ether, trx def call_signed(signer, client, ethTrx, perm_accs, steps): @@ -1088,7 +1087,7 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, str, str]: logger.debug(f"Signer: {signer}") - (sol, nonce) = ether2program(ether, evm_loader_id, signer.public_key()) + sol, nonce = ether2program(ether, evm_loader_id, signer.public_key()) associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) logger.debug('associatedTokenAccount: {}'.format(associated_token)) @@ -1206,23 +1205,20 @@ def getLamports(client, evm_loader, eth_acc, base_account): (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) return int(client.get_balance(account, commitment=Confirmed)['result']['value']) - def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, trx: Transaction): dest_token_account = getTokenAddr(dest_account) - owner_addr = owner_account.public_key() - owner_token_addr = getTokenAddr(owner_addr) + owner_sol_addr = owner_account.public_key() + owner_token_addr = getTokenAddr(owner_sol_addr) transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, - owner=owner_addr, + owner=owner_sol_addr, dest=dest_token_account, - amount=NEW_USER_AIRDROP_AMOUNT * GWEI_PER_ETH_COUNT, + amount=NEW_USER_AIRDROP_AMOUNT * eth_utils.denoms.gwei, decimals=9, mint=ETH_TOKEN_MINT_ID, program_id=TOKEN_PROGRAM_ID)) + logger.debug(f"Token transfer from token: {owner_token_addr}, owned by: {owner_sol_addr}, to token: " + f"{dest_token_account}, owned by: {dest_account} , value: {NEW_USER_AIRDROP_AMOUNT}") trx.add(transfer_instruction) - # logger.debug("Token transfer from: to %s as ethereum 0x%s amount %s", - # get_associated_token_address(PublicKey(acc_desc["account"]), ETH_TOKEN_MINT_ID), - # acc_desc["address"], - # str(NEW_USER_AIRDROP_AMOUNT)) def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: str, trx: Transaction, code_acc=None): @@ -1230,7 +1226,6 @@ def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_a logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}, at token: {ETH_TOKEN_MINT_ID}") trx.add(create_trx) add_airdrop_transfer_to_trx(signer, solana_address, trx) - logger.debug(f"Token transfer from: {signer.public_key()}, to eth account: {eth_acc}, aka: {solana_address}, token_address: {token_address}, value: {NEW_USER_AIRDROP_AMOUNT}") def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): From e14a6b1c5c36afb32be87e498b90cf860a00655d Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 17:44:09 +0500 Subject: [PATCH 16/95] extend airdrop tests --- proxy/plugin/solana_rest_api.py | 45 +++++---- proxy/plugin/solana_rest_api_tools.py | 15 +-- .../testing/test_airdropping_eth_accounts.py | 98 +++++++++++++------ 3 files changed, 103 insertions(+), 55 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 0620c4445..d9493fcf5 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -8,7 +8,7 @@ :copyright: (c) 2013-present by Abhinav Singh and contributors. :license: BSD, see LICENSE for more details. """ -from typing import List, Tuple +from typing import List, Tuple, Optional import json import unittest import rlp @@ -25,7 +25,7 @@ import base58 import traceback import threading -from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, get_token_balance, \ +from .solana_rest_api_tools import EthereumAddress, create_account_with_seed, evm_loader_id, get_token_balance_or_airdrop, \ getAccountInfo, solana_cli, call_signed, solana_url, call_emulated, \ Trx, EthereumError, create_collateral_pool_address, getTokenAddr, STORAGE_SIZE, neon_config_load from solana.rpc.commitment import Commitment, Confirmed @@ -69,22 +69,8 @@ def __init__(self, client, signer, proxy_id): class EthereumModel: def __init__(self): # Initialize user account - res = solana_cli().call('config', 'get') - substr = "Keypair Path: " - path = "" - for line in res.splitlines(): - if line.startswith(substr): - path = line[len(substr):].strip() - if path == "": - raise Exception("cannot get keypair path") - - with open(path.strip(), mode='r') as file: - pk = (file.read()) - nums = list(map(int, pk.strip("[] \n").split(','))) - nums = nums[0:32] - values = bytes(nums) - self.signer = sol_Account(values) + self.signer = self.get_solana_account() self.client = SolanaClient(solana_url) self.logs_db = LogDB(filename="local.db") @@ -102,6 +88,26 @@ def __init__(self): neon_config_load(self) pass + @staticmethod + def get_solana_account() -> Optional[sol_Account]: + solana_account = None + res = solana_cli().call('config', 'get') + substr = "Keypair Path: " + path = "" + for line in res.splitlines(): + if line.startswith(substr): + path = line[len(substr):].strip() + if path == "": + raise Exception("cannot get keypair path") + + with open(path.strip(), mode='r') as file: + pk = (file.read()) + nums = list(map(int, pk.strip("[] \n").split(','))) + nums = nums[0:32] + values = bytes(nums) + solana_account = sol_Account(values) + return solana_account + def web3_clientVersion(self): neon_config_load(self) return self.neon_config_dict['web3_clientVersion'] @@ -150,7 +156,7 @@ def eth_getBalance(self, account, tag): """ eth_acc = EthereumAddress(account) logger.debug('eth_getBalance: %s %s', account, eth_acc) - balance = get_token_balance(self.client, self.signer, evm_loader_id, eth_acc, self.signer.public_key()) + balance = get_token_balance_or_airdrop(self.client, self.signer, evm_loader_id, eth_acc) return hex(balance*10**9) @@ -478,12 +484,11 @@ def default(self, obj): class SolanaContractTests(unittest.TestCase): + def setUp(self): self.model = EthereumModel() self.owner = '0xc1566af4699928fdf9be097ca3dc47ece39f8f8e' self.token1 = '0x49a449cd7fd8fbcf34d103d98f2c05245020e35b' -# self.assertEqual(self.getBalance(self.owner), 1000*10**18) -# self.assertEqual(self.getBalance(self.token1), 0) def getBalance(self, account): return int(self.model.eth_getBalance(account, 'latest'), 16) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 26530a8e9..fe54dca84 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1238,8 +1238,9 @@ def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, eth_acc raise Exception("Create account error") -def get_token_balance_impl(client: SolanaClient, account: str, eth_acc: EthereumAddress) -> [Optional[int], Optional[Union[Dict, str]]]: - token_account = get_associated_token_address(PublicKey(account), ETH_TOKEN_MINT_ID) +def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str, eth_acc: EthereumAddress) \ + -> [Optional[int], Optional[Union[Dict, str]]]: + token_account = get_associated_token_address(PublicKey(token_owner_acc), ETH_TOKEN_MINT_ID) rpc_response = client.get_token_account_balance(token_account, commitment=Confirmed) error = rpc_response.get('error') if error is None: @@ -1247,23 +1248,23 @@ def get_token_balance_impl(client: SolanaClient, account: str, eth_acc: Ethereum if balance is None: return None, f"Failed to get token balance from: {rpc_response}, by eth account:" \ f" {eth_acc} aka: {token_account} at token: {ETH_TOKEN_MINT_ID}" - return balance, None + return int(balance), None return None, error -def get_token_balance(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress, base_account: PublicKey) -> int: +def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress) -> int: - account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) + account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, signer.public_key()) logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - balance, error = get_token_balance_impl(client, account, eth_acc) + balance, error = get_token_balance_gwei(client, account, eth_acc) if error is None: return int(balance) if error.get("message") == SolanaErrors.AccountNotFound.value and NEW_USER_AIRDROP_AMOUNT > 0: logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") create_and_mint_tkn_acc(client, signer, eth_acc) - balance, error = get_token_balance_impl(client, account, eth_acc) + balance, error = get_token_balance_gwei(client, account, eth_acc) if error is None: return int(balance) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 3b3e76f9e..f41d1a99d 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -1,53 +1,80 @@ import unittest +import os +import solcx + import eth_account import eth_typing import eth_utils -import os -from web3 import Web3 -import solcx -import logging +from eth_account.account import LocalAccount + +from web3 import Web3, eth as web3_eth +from solana.rpc.api import Client as SolanaClient -NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) +from ..plugin.solana_rest_api import EthereumModel +from ..plugin.solana_rest_api_tools import get_token_balance_gwei, EthereumAddress, ether2program class TestAirdroppingEthAccounts(unittest.TestCase): @classmethod def setUpClass(cls) -> None: + cls._EVM_LOADER_ID = os.environ.get("EVM_LOADER") + new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) + cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - cls.proxy = Web3(Web3.HTTPProvider(proxy_url)) + cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) + solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") + cls._solana_client = SolanaClient(solana_url) + cls._host_solana_account = EthereumModel.get_solana_account() def test_airdrop_on_get_balance(self): - account: eth_account.account.LocalAccount = eth_account.account.Account.create() - block_number: eth_typing.BlockNumber = self.proxy.eth.get_block_number() - actual_balance_wei = self.proxy.eth.get_balance(account.address, block_identifier=block_number) - expected_balance_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') - self.assertEqual(expected_balance_wei, actual_balance_wei) + account: LocalAccount = eth_account.account.Account.create() + block_number: eth_typing.BlockNumber = self._web3.eth.get_block_number() + actual_balance_wei = self._web3.eth.get_balance(account.address, block_identifier=block_number) + self.assertEqual(self._EXPECTED_BALANCE_WEI, actual_balance_wei) def test_airdrop_on_deploy(self): - contract_owner: eth_account.account.LocalAccount = self.proxy.eth.account.create() - compiled_sol = solcx.compile_source(self._CONTRACT_STORAGE_SOURCE) + contract_owner: LocalAccount = self._web3.eth.account.create() + contract = self._compile_and_deploy_contract(contract_owner, self._CONTRACT_STORAGE_SOURCE) + actual_balance_wei = self._get_balance_wei(contract.address) + self.assertEqual(self._EXPECTED_BALANCE_WEI, actual_balance_wei) + + def test_airdrop_onto_wrapped_new_address(self): + contract_owner: LocalAccount = self._web3.eth.account.create() + contract = self._compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) + nested_contract_address = contract.functions.getNested().call() + nested_actual_balance = self._get_balance_wei(nested_contract_address) + wrapper_actual_balance = self._get_balance_wei(contract.address) + self.assertEqual(self._EXPECTED_BALANCE_WEI, wrapper_actual_balance) + self.assertEqual(self._EXPECTED_BALANCE_WEI, nested_actual_balance) + + def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str) -> web3_eth.Contract: + compiled_sol = solcx.compile_source(source) contract_id, contract_interface = compiled_sol.popitem() - storage = self.proxy.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) - nonce = self.proxy.eth.get_transaction_count(contract_owner.address) - chain_id = self.proxy.eth.chain_id - trx_signed = self.proxy.eth.account.sign_transaction( - dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=0, to='', value=0, data=storage.bytecode), + contract = self._web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + nonce = self._web3.eth.get_transaction_count(contract_owner.address) + chain_id = self._web3.eth.chain_id + trx_signed = self._web3.eth.account.sign_transaction( + dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=0, to='', value=0, data=contract.bytecode), contract_owner.key ) - trx_hash = self.proxy.eth.send_raw_transaction(trx_signed.rawTransaction) - trx_receipt = self.proxy.eth.wait_for_transaction_receipt(trx_hash) - storage_contract = self.proxy.eth.contract( + trx_hash = self._web3.eth.send_raw_transaction(trx_signed.rawTransaction) + trx_receipt = self._web3.eth.wait_for_transaction_receipt(trx_hash) + contract = self._web3.eth.contract( address=trx_receipt.contractAddress, - abi=storage.abi + abi=contract.abi ) - actual_balance_wei = self.proxy.eth.get_balance(storage_contract.address, block_identifier="latest") - expected_balance_wei = eth_utils.to_wei(NEW_USER_AIRDROP_AMOUNT, 'ether') + return contract - owner_balance = self.proxy.eth.get_balance(contract_owner.address, block_identifier="latest") - - self.assertEqual(expected_balance_wei, owner_balance) - self.assertEqual(expected_balance_wei, actual_balance_wei) + def _get_balance_wei(self, eth_acc: str) -> int: + pub_key = self._host_solana_account.public_key() + token_owner_account, nonce = ether2program(eth_acc, self._EVM_LOADER_ID, pub_key) + balance, error = get_token_balance_gwei(self._solana_client, token_owner_account, EthereumAddress(eth_acc)) + self.assertIsNone(error) + self.assertIsNotNone(balance) + self.assertIsInstance(balance, int) + return balance * eth_utils.denoms.gwei _CONTRACT_STORAGE_SOURCE = '''pragma solidity >=0.7.0 <0.9.0; contract Storage { @@ -61,3 +88,18 @@ def test_airdrop_on_deploy(self): } ''' + _WRAPPER_CONTRACT_STORAGE_SOURCE = ''' + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; + contract Wrapper { + address private nested_address; + constructor() { + Nested nested = new Nested(); + nested_address = address(nested); + } + function getNested() public view returns (address) { + return nested_address; + } + } + contract Nested {} + ''' From 0b0046eddb2794413dd4d1900eccb45b8a5cfe05 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 17:48:29 +0500 Subject: [PATCH 17/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index fe54dca84..b245f71d1 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -931,7 +931,7 @@ def create_account_list_by_emulate(signer, client, ethTrx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - create_and_mint_token_trx(client, signer, EthereumAddress(address), trx, code_account) + create_token_and_airdrop_trx(client, signer, EthereumAddress(address), trx, code_account) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1221,16 +1221,18 @@ def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, trx.add(transfer_instruction) -def create_and_mint_token_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: str, trx: Transaction, code_acc=None): +def create_token_and_airdrop_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, + trx: Transaction, code_acc=None): create_trx, solana_address, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) - logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}, at token: {ETH_TOKEN_MINT_ID}") + logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}," + f" at token: {ETH_TOKEN_MINT_ID}") trx.add(create_trx) add_airdrop_transfer_to_trx(signer, solana_address, trx) -def create_and_mint_tkn_acc(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): +def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): trx = Transaction() - create_and_mint_token_trx(client, signer, eth_acc, trx) + create_token_and_airdrop_trx(client, signer, eth_acc, trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: @@ -1263,7 +1265,7 @@ def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, ev if error.get("message") == SolanaErrors.AccountNotFound.value and NEW_USER_AIRDROP_AMOUNT > 0: logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - create_and_mint_tkn_acc(client, signer, eth_acc) + create_token_and_airdrop(client, signer, eth_acc) balance, error = get_token_balance_gwei(client, account, eth_acc) if error is None: return int(balance) From d9c677b4e907679159e058b48645609c9887e46b Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 18:14:07 +0500 Subject: [PATCH 18/95] spit and polish --- proxy/plugin/solana_rest_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 7e45c6e2f..1505a3aae 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -90,7 +90,7 @@ def __init__(self): @staticmethod def get_solana_account() -> Optional[sol_Account]: - solana_account = None + solana_account: Optional[sol_Account] = None res = solana_cli().call('config', 'get') substr = "Keypair Path: " path = "" From 9d411c83c4cb22d6d1dd2af135e3c4562852b826 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 18:33:57 +0500 Subject: [PATCH 19/95] move tests --- proxy/{ => testing}/test_create_account_block.py | 0 proxy/{ => testing}/test_environment.py | 0 proxy/{ => testing}/test_neon_faucet.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename proxy/{ => testing}/test_create_account_block.py (100%) rename proxy/{ => testing}/test_environment.py (100%) rename proxy/{ => testing}/test_neon_faucet.py (100%) diff --git a/proxy/test_create_account_block.py b/proxy/testing/test_create_account_block.py similarity index 100% rename from proxy/test_create_account_block.py rename to proxy/testing/test_create_account_block.py diff --git a/proxy/test_environment.py b/proxy/testing/test_environment.py similarity index 100% rename from proxy/test_environment.py rename to proxy/testing/test_environment.py diff --git a/proxy/test_neon_faucet.py b/proxy/testing/test_neon_faucet.py similarity index 100% rename from proxy/test_neon_faucet.py rename to proxy/testing/test_neon_faucet.py From e9a18b7bb7351f5cd6a55251576a457602bf62b4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 18:59:00 +0500 Subject: [PATCH 20/95] move tests --- proxy/common/constants.py | 2 -- proxy/testing/test_eth_sendRawTransaction.py | 25 ++++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/proxy/common/constants.py b/proxy/common/constants.py index 48c35a691..1479523a7 100644 --- a/proxy/common/constants.py +++ b/proxy/common/constants.py @@ -86,5 +86,3 @@ PLUGIN_DEVTOOLS_PROTOCOL = 'proxy.http.inspector.DevtoolsProtocolPlugin' PLUGIN_DASHBOARD = 'proxy.dashboard.dashboard.ProxyDashboard' PLUGIN_INSPECT_TRAFFIC = 'proxy.dashboard.inspect_traffic.InspectTrafficPlugin' - -GWEI_PER_ETH_COUNT = 1_000_000_000 diff --git a/proxy/testing/test_eth_sendRawTransaction.py b/proxy/testing/test_eth_sendRawTransaction.py index 538ed55cc..f59cbdbb2 100644 --- a/proxy/testing/test_eth_sendRawTransaction.py +++ b/proxy/testing/test_eth_sendRawTransaction.py @@ -1,8 +1,9 @@ import unittest import os + +import eth_utils from web3 import Web3 from solcx import install_solc -from ..common.constants import GWEI_PER_ETH_COUNT # install_solc(version='latest') install_solc(version='0.7.0') @@ -253,7 +254,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_alice.address, - value=GWEI_PER_ETH_COUNT), + value=eth_utils.denoms.gwei), eth_account.key ) @@ -270,7 +271,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=GWEI_PER_ETH_COUNT), + value=eth_utils.denoms.gwei), eth_account.key ) @@ -284,7 +285,7 @@ def test_05_transfer_one_gwei(self): bob_balance_before_transfer = proxy.eth.get_balance(eth_account_bob.address) print('alice_balance_before_transfer:', alice_balance_before_transfer) print('bob_balance_before_transfer:', bob_balance_before_transfer) - print('one_gwei:', GWEI_PER_ETH_COUNT) + print('one_gwei:', eth_utils.denoms.gwei) trx_transfer = proxy.eth.account.sign_transaction(dict( nonce=proxy.eth.get_transaction_count(eth_account_alice.address), @@ -292,7 +293,7 @@ def test_05_transfer_one_gwei(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=GWEI_PER_ETH_COUNT), + value=eth_utils.denoms.gwei), eth_account_alice.key ) @@ -306,8 +307,8 @@ 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 - GWEI_PER_ETH_COUNT) - self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + GWEI_PER_ETH_COUNT) + self.assertEqual(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.") def test_06_transfer_one_and_a_half_gweis(self): @@ -328,7 +329,7 @@ def test_06_transfer_one_and_a_half_gweis(self): gas=987654321, gasPrice=0, to=eth_account_alice.address, - value=GWEI_PER_ETH_COUNT), + value=eth_utils.denoms.gwei), eth_account.key ) @@ -345,7 +346,7 @@ def test_06_transfer_one_and_a_half_gweis(self): gas=987654321, gasPrice=0, to=eth_account_bob.address, - value=GWEI_PER_ETH_COUNT), + value=eth_utils.denoms.gwei), eth_account.key ) @@ -383,9 +384,9 @@ def test_06_transfer_one_and_a_half_gweis(self): print('alice_balance_after_transfer:', alice_balance_after_transfer) print('bob_balance_after_transfer:', bob_balance_after_transfer) print('check https://github.com/neonlabsorg/neon-evm/issues/210') - print('one_gwei:', GWEI_PER_ETH_COUNT) - self.assertEqual(alice_balance_after_transfer, alice_balance_before_transfer - GWEI_PER_ETH_COUNT) - self.assertEqual(bob_balance_after_transfer, bob_balance_before_transfer + GWEI_PER_ETH_COUNT) + print('one_gwei:', eth_utils.denoms.gwei) + self.assertEqual(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.") def test_07_execute_long_transaction(self): From 343cedb8178fa65dd9e65b2092ef666ee7f6150d Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 10 Nov 2021 19:00:08 +0500 Subject: [PATCH 21/95] Get rid off extra data.py --- proxy/common/data.py | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 proxy/common/data.py diff --git a/proxy/common/data.py b/proxy/common/data.py deleted file mode 100644 index ce90b3b18..000000000 --- a/proxy/common/data.py +++ /dev/null @@ -1,6 +0,0 @@ -from dataclasses import dataclass - -@dataclass -class TokenAccountInfo: - eth_account: str - associated_account: From f1b8efbf5996712caf1690af9452714bece44596 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 14:02:20 +0500 Subject: [PATCH 22/95] Improve logging --- proxy/plugin/solana_rest_api.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 1505a3aae..91cb70380 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -20,7 +20,7 @@ from ..http.websocket import WebsocketFrame from ..http.server import HttpWebServerBasePlugin, httpProtocolTypes from .eth_proto import Trx as EthTrx -from solana.rpc.api import Client as SolanaClient +from solana.rpc.api import Client as SolanaClient, SendTransactionError as SolanaTrxError from sha3 import keccak_256 import base58 import traceback @@ -37,6 +37,7 @@ from ..indexer.sql_dict import SQLDict from proxy.environment import evm_loader_id, solana_cli, solana_url + logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -45,6 +46,7 @@ EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "0")) + class PermanentAccounts: def __init__(self, client, signer, proxy_id): self.operator = signer.public_key() @@ -469,9 +471,8 @@ def eth_sendRawTransaction(self, rawTrx): return eth_signature - except solana.rpc.api.SendTransactionError as err: - logs = "\n\t".join(err.result.get("data", {}).pop("logs", [])) - logger.debug(f"eth_sendRawTransaction solana.rpc.api.SendTransactionError: {err.result}, {logs}") + except SolanaTrxError as err: + self._log_transaction_error(err, logger) raise except EthereumError as err: logger.debug("eth_sendRawTransaction EthereumError:%s", err) @@ -480,6 +481,13 @@ def eth_sendRawTransaction(self, rawTrx): logger.debug("eth_sendRawTransaction type(err):%s, Exception:%s", type(err), err) raise + def _log_transaction_error(self, error: SolanaTrxError, logger): + copy.deep_copy(error) + logs = error.result.get("data", {}).get("logs", []) + error.result.get("data", {}).update({"logs": ["\n\t" + log for log in logs]}) + log_msg = str(error.result).replace("\\n\\t", "\n\t") + logger.debug(f"Got SendTransactionError: {log_msg}") + class JsonEncoder(json.JSONEncoder): def default(self, obj): @@ -585,7 +593,7 @@ def process_request(self, request): try: method = getattr(self.model, request['method']) response['result'] = method(*request['params']) - except solana.rpc.api.SendTransactionError as err: + except SolanaTrxError as err: traceback.print_exc() response['error'] = err.result except EthereumError as err: From df19a621cc0e8b5d4a3c8d9ea62740378e547807 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 14:22:27 +0500 Subject: [PATCH 23/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 21deb7e02..5a58d56d7 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1042,7 +1042,6 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, str, str]: - logger.debug(f"Signer: {signer}") sol, nonce = ether2program(ether, evm_loader_id, signer.public_key()) associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) From 4a390d11e31882f7c619462d3e8eb3a368eb655b Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 14:31:42 +0500 Subject: [PATCH 24/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 5a58d56d7..84a463667 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1042,15 +1042,16 @@ def call_signed_with_holder_acc(signer, client, ethTrx, perm_accs, trx_accs, ste def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, str, str]: - sol, nonce = ether2program(ether, evm_loader_id, signer.public_key()) - associated_token = get_associated_token_address(PublicKey(sol), ETH_TOKEN_MINT_ID) - logger.debug('createEtherAccount: {} {} => {}'.format(ether, nonce, sol)) - logger.debug('associatedTokenAccount: {}'.format(associated_token)) + solana_address, nonce = ether2program(ether, evm_loader_id, signer.public_key()) - sender_sol_info = client.get_account_info(sol, commitment=Confirmed) + token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) + + logger.debug(f'Create eth account: {ether}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') + + sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) value = get_from_dict(sender_sol_info, "result", "value") if value is not None: - logger.error(f"Failed to create eth account: {ether}, associated: {associated_token}, already exists") + logger.error(f"Failed to create eth account: {ether}, associated: {token_acc_address}, already exists") raise Exception("Account already exists") base = signer.public_key() @@ -1066,8 +1067,8 @@ def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: E data=data, keys=[ AccountMeta(pubkey=base, is_signer=True, is_writable=True), - AccountMeta(pubkey=PublicKey(sol), is_signer=False, is_writable=True), - AccountMeta(pubkey=associated_token, is_signer=False, is_writable=True), + AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), + AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), AccountMeta(pubkey=system, is_signer=False, is_writable=False), AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), @@ -1080,8 +1081,8 @@ def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: E data=data, keys=[ AccountMeta(pubkey=base, is_signer=True, is_writable=True), - AccountMeta(pubkey=PublicKey(sol), is_signer=False, is_writable=True), - AccountMeta(pubkey=associated_token, is_signer=False, is_writable=True), + AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), + AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), AccountMeta(pubkey=PublicKey(code_acc), is_signer=False, is_writable=True), AccountMeta(pubkey=system, is_signer=False, is_writable=False), AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), @@ -1089,7 +1090,7 @@ def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: E AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), ])) - return trx, sol, associated_token + return trx, solana_address, token_acc_address def createERC20TokenAccountTrx(signer, token_info): From 31c7ddb807ddd0e1d4979528e8c8a5d424ccc1b7 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 14:44:45 +0500 Subject: [PATCH 25/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index e4ed13342..bc6613846 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -868,7 +868,7 @@ def update_transaction_cost(receipt, eth_trx, extra_sol_trx=False, reason=None): def create_account_list_by_emulate(signer, client, eth_trx): - sender_ether = bytes.fromhex(>eth_trx.sender()) + sender_ether = bytes.fromhex(eth_trx.sender()) add_keys_05 = [] trx = Transaction() From b6927e9bcaea2854657d7976d1e71081c14027f8 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 15:43:43 +0500 Subject: [PATCH 26/95] spit and polish --- proxy/plugin/solana_rest_api.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 6f4c5391f..aee4dfaa1 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -70,8 +70,6 @@ def __init__(self, client, signer, proxy_id): class EthereumModel: def __init__(self): - # Initialize user account - self.signer = self.get_solana_account() self.client = SolanaClient(solana_url) @@ -88,7 +86,6 @@ def __init__(self): self.perm_accs = PermanentAccounts(self.client, self.signer, self.proxy_id) neon_config_load(self) - pass @staticmethod def get_solana_account() -> Optional[sol_Account]: From c394edb43f273ea4f1070b345219bd871501fa5d Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 16:33:43 +0500 Subject: [PATCH 27/95] Pass MINIMAL_GAS_PRICE int airdrop tests --- proxy/plugin/solana_rest_api.py | 1 + proxy/testing/test_airdropping_eth_accounts.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index aee4dfaa1..2e4ed53d4 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for more details. """ from typing import List, Tuple, Optional +import copy import json import unittest import rlp diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index f41d1a99d..597a90b20 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -21,6 +21,7 @@ def setUpClass(cls) -> None: cls._EVM_LOADER_ID = os.environ.get("EVM_LOADER") new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') + cls._MINIMAL_GAS_PRICE = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) @@ -56,7 +57,7 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str nonce = self._web3.eth.get_transaction_count(contract_owner.address) chain_id = self._web3.eth.chain_id trx_signed = self._web3.eth.account.sign_transaction( - dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=0, to='', value=0, data=contract.bytecode), + dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=self._MINIMAL_GAS_PRICE, to='', value=0, data=contract.bytecode), contract_owner.key ) trx_hash = self._web3.eth.send_raw_transaction(trx_signed.rawTransaction) From 0b97d84250ae7bfe84ab6c6f28dc39a83c2e2df3 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 16:35:30 +0500 Subject: [PATCH 28/95] spit and polish --- proxy/testing/test_airdropping_eth_accounts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 597a90b20..ba07630e8 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -77,7 +77,9 @@ def _get_balance_wei(self, eth_acc: str) -> int: self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei - _CONTRACT_STORAGE_SOURCE = '''pragma solidity >=0.7.0 <0.9.0; + _CONTRACT_STORAGE_SOURCE = ''' + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; contract Storage { uint256 number; function store(uint256 num) public { From e1bf41691125c58a81917b65c94c0f6ed9b038cf Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 17:10:31 +0500 Subject: [PATCH 29/95] move test_operator_spending.py --- proxy/testing/__init__.py | 2 ++ proxy/testing/test_cancel_hanged.py | 1 - proxy/{ => testing}/test_operator_spending.py | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename proxy/{ => testing}/test_operator_spending.py (100%) diff --git a/proxy/testing/__init__.py b/proxy/testing/__init__.py index 232621f0b..896651028 100644 --- a/proxy/testing/__init__.py +++ b/proxy/testing/__init__.py @@ -8,3 +8,5 @@ :copyright: (c) 2013-present by Abhinav Singh and contributors. :license: BSD, see LICENSE for more details. """ +import sys +sys.path.append("/spl/bin/") diff --git a/proxy/testing/test_cancel_hanged.py b/proxy/testing/test_cancel_hanged.py index 241f15728..1e91a1381 100644 --- a/proxy/testing/test_cancel_hanged.py +++ b/proxy/testing/test_cancel_hanged.py @@ -3,7 +3,6 @@ from proxy.plugin.solana_rest_api_tools import sysinstruct, ETH_TOKEN_MINT_ID, system, send_transaction, MINIMAL_GAS_PRICE -sys.path.append("/spl/bin/") os.environ['SOLANA_URL'] = "http://solana:8899" os.environ['EVM_LOADER'] = "53DfF883gyixYNXnM7s5xhdeyV8mVk9T4i2hGV9vG9io" os.environ['ETH_TOKEN_MINT'] = "HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU" diff --git a/proxy/test_operator_spending.py b/proxy/testing/test_operator_spending.py similarity index 100% rename from proxy/test_operator_spending.py rename to proxy/testing/test_operator_spending.py From c1321fc813ada0b76c20c4ccab634d9fa58ad1b5 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 17:17:35 +0500 Subject: [PATCH 30/95] move test_operator_spending.py --- proxy/testing/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/testing/__init__.py b/proxy/testing/__init__.py index 896651028..ecd1ab354 100644 --- a/proxy/testing/__init__.py +++ b/proxy/testing/__init__.py @@ -9,4 +9,4 @@ :license: BSD, see LICENSE for more details. """ import sys -sys.path.append("/spl/bin/") +sys.path.append("/spl/bin/") # TODO: get rid off this workaround all related py should be linked from submodule From 517c133b833b0ca16d27dedbc9679a83798e74e7 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 17:28:54 +0500 Subject: [PATCH 31/95] spit and polish --- proxy/testing/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/testing/__init__.py b/proxy/testing/__init__.py index ecd1ab354..0bfb50319 100644 --- a/proxy/testing/__init__.py +++ b/proxy/testing/__init__.py @@ -9,4 +9,4 @@ :license: BSD, see LICENSE for more details. """ import sys -sys.path.append("/spl/bin/") # TODO: get rid off this workaround all related py should be linked from submodule +sys.path.append("/spl/bin/") # TODO: get rid off this workaround all related modules should either be installed as package or be linked from submodule From 644a64faf439448ee3ec665f65b8bc333d5d1007 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 17:36:17 +0500 Subject: [PATCH 32/95] spit and polish --- proxy/plugin/solana_rest_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 2e4ed53d4..e1a91d086 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -36,7 +36,7 @@ import os from ..indexer.utils import get_trx_results, LogDB from ..indexer.sql_dict import SQLDict -from proxy.environment import evm_loader_id, solana_cli, solana_url +from ..environment import evm_loader_id, solana_cli, solana_url logger = logging.getLogger(__name__) From 7c5ac6187bcd4bb3584539f236cb8b3689d5d45c Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 17:51:46 +0500 Subject: [PATCH 33/95] Fix message printing --- proxy/plugin/solana_rest_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index e1a91d086..860b360ac 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -480,10 +480,10 @@ def eth_sendRawTransaction(self, rawTrx): raise def _log_transaction_error(self, error: SolanaTrxError, logger): - copy.deep_copy(error) - logs = error.result.get("data", {}).get("logs", []) - error.result.get("data", {}).update({"logs": ["\n\t" + log for log in logs]}) - log_msg = str(error.result).replace("\\n\\t", "\n\t") + result = copy.deepcopy(error.result) + logs = result.get("data", {}).get("logs", []) + result.get("data", {}).update({"logs": ["\n\t" + log for log in logs]}) + log_msg = str(result).replace("\\n\\t", "\n\t") logger.debug(f"Got SendTransactionError: {log_msg}") From 85e89fea96a8ca488f8d5bd6382c46702b3f62bd Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 18:30:09 +0500 Subject: [PATCH 34/95] spit and polish --- proxy/environment.py | 4 ++-- proxy/plugin/solana_rest_api_tools.py | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/proxy/environment.py b/proxy/environment.py index df86e2042..7ba268c0a 100644 --- a/proxy/environment.py +++ b/proxy/environment.py @@ -16,7 +16,7 @@ def call(self, *args): cmd = ["solana", "--url", solana_url, ] + list(args) - print(cmd) + logger.debug("Calling: " + " ".join(cmd)) return subprocess.check_output(cmd, universal_newlines=True) except subprocess.CalledProcessError as err: import sys @@ -32,7 +32,7 @@ def call(self, *args): "--url", solana_url, "--evm_loader={}".format(evm_loader_id), ] + list(args) - print(cmd) + logger.debug("Calling: " + " ".join(cmd)) return subprocess.check_output(cmd, timeout=neon_cli_timeout, universal_newlines=True) except subprocess.CalledProcessError as err: import sys diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index bc6613846..19e5479ce 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1105,12 +1105,10 @@ def call_signed_with_holder_acc(signer, client, eth_trx, perm_accs, trx_accs, st return call_continue(signer, client, perm_accs, trx_accs, steps, eth_trx) -def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, str, str]: +def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: solana_address, nonce = ether2program(ether, evm_loader_id, signer.public_key()) - token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) - logger.debug(f'Create eth account: {ether}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) @@ -1155,7 +1153,7 @@ def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: E AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), ])) - return trx, solana_address, token_acc_address + return trx, token_acc_address def createERC20TokenAccountTrx(signer, token_info): @@ -1228,8 +1226,7 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, trx: Transaction): - dest_token_account = getTokenAddr(dest_account) +def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_token_account: PublicKey, trx: Transaction): owner_sol_addr = owner_account.public_key() owner_token_addr = getTokenAddr(owner_sol_addr) transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, @@ -1240,17 +1237,15 @@ def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_account: str, mint=ETH_TOKEN_MINT_ID, program_id=TOKEN_PROGRAM_ID)) logger.debug(f"Token transfer from token: {owner_token_addr}, owned by: {owner_sol_addr}, to token: " - f"{dest_token_account}, owned by: {dest_account} , value: {NEW_USER_AIRDROP_AMOUNT}") + f"{dest_token_account}, owned by: {dest_token_account} , value: {NEW_USER_AIRDROP_AMOUNT}") trx.add(transfer_instruction) def create_token_and_airdrop_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, trx: Transaction, code_acc=None): - create_trx, solana_address, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) - logger.debug(f"Create token related to eth_acc: {eth_acc}, aka: {solana_address}, token_address: {token_address}," - f" at token: {ETH_TOKEN_MINT_ID}") + create_trx, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) trx.add(create_trx) - add_airdrop_transfer_to_trx(signer, solana_address, trx) + add_airdrop_transfer_to_trx(signer, token_address, trx) def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): From 790e6a55db8f91ad18945344675993bd6a508adc Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 18:53:02 +0500 Subject: [PATCH 35/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 19e5479ce..9b4117ff8 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1105,23 +1105,23 @@ def call_signed_with_holder_acc(signer, client, eth_trx, perm_accs, trx_accs, st return call_continue(signer, client, perm_accs, trx_accs, steps, eth_trx) -def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, ether: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: +def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: - solana_address, nonce = ether2program(ether, evm_loader_id, signer.public_key()) + solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) - logger.debug(f'Create eth account: {ether}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') + logger.debug(f'Create eth account: {eth_address}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) value = get_from_dict(sender_sol_info, "result", "value") if value is not None: - logger.error(f"Failed to create eth account: {ether}, associated: {token_acc_address}, already exists") + logger.error(f"Failed to create eth account: {eth_address}, associated: {token_acc_address}, already exists") raise Exception("Account already exists") base = signer.public_key() data = bytes.fromhex('02000000') + CREATE_ACCOUNT_LAYOUT.build(dict(lamports=0, space=0, - ether=bytes(ether), + ether=bytes(eth_address), nonce=nonce)) trx = Transaction() if code_acc is None: From 26d58f79a2af5ae07ff5ddb2aede9ad126600224 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 20:34:22 +0500 Subject: [PATCH 36/95] spit and polish --- proxy/testing/test_airdropping_eth_accounts.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index ba07630e8..43850aaa7 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -58,14 +58,10 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str chain_id = self._web3.eth.chain_id trx_signed = self._web3.eth.account.sign_transaction( dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=self._MINIMAL_GAS_PRICE, to='', value=0, data=contract.bytecode), - contract_owner.key - ) + contract_owner.key) trx_hash = self._web3.eth.send_raw_transaction(trx_signed.rawTransaction) trx_receipt = self._web3.eth.wait_for_transaction_receipt(trx_hash) - contract = self._web3.eth.contract( - address=trx_receipt.contractAddress, - abi=contract.abi - ) + contract = self._web3.eth.contract(address=trx_receipt.contractAddress, abi=contract.abi) return contract def _get_balance_wei(self, eth_acc: str) -> int: From 39ed586b1fc1f75380dee43d8cefdda501f420a1 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 11 Nov 2021 20:35:37 +0500 Subject: [PATCH 37/95] spit and polish --- proxy/testing/test_erc20_wrapper_contract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/testing/test_erc20_wrapper_contract.py b/proxy/testing/test_erc20_wrapper_contract.py index 2b05c34f2..75030130b 100644 --- a/proxy/testing/test_erc20_wrapper_contract.py +++ b/proxy/testing/test_erc20_wrapper_contract.py @@ -15,7 +15,7 @@ from solana.account import Account as SolanaAccount from solana.publickey import PublicKey -from proxy.plugin.solana_rest_api_tools import createERC20TokenAccountTrx, create_eth_account_trx +from proxy.plugin.solana_rest_api_tools import createERC20TokenAccountTrx # install_solc(version='latest') install_solc(version='0.7.6') From 955d0aacc85095f922a9bf35700930347ee5b810 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 12 Nov 2021 12:06:57 +0500 Subject: [PATCH 38/95] use error instead of debug --- proxy/plugin/solana_rest_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 860b360ac..a813df585 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -484,7 +484,7 @@ def _log_transaction_error(self, error: SolanaTrxError, logger): logs = result.get("data", {}).get("logs", []) result.get("data", {}).update({"logs": ["\n\t" + log for log in logs]}) log_msg = str(result).replace("\\n\\t", "\n\t") - logger.debug(f"Got SendTransactionError: {log_msg}") + logger.error(f"Got SendTransactionError: {log_msg}") class JsonEncoder(json.JSONEncoder): From a22639b71e3dda68faad9ba4f4c5c601c348c9cd Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 08:50:35 +0500 Subject: [PATCH 39/95] Revert "constants and utils" This reverts commit 505653656a5406d64592c4fa2ebcf8ff1aed8edd. # Conflicts: # proxy/testing/test_eth_sendRawTransaction.py --- proxy/common_neon/utils.py | 13 +++++++++++++ proxy/plugin/solana_rest_api_tools.py | 2 +- proxy/testing/test_utils.py | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 proxy/common_neon/utils.py diff --git a/proxy/common_neon/utils.py b/proxy/common_neon/utils.py new file mode 100644 index 000000000..be93827e1 --- /dev/null +++ b/proxy/common_neon/utils.py @@ -0,0 +1,13 @@ +from typing import Dict, Optional, Any + + +def get_from_dict(src: Dict, *path) -> Optional[Any]: + """Provides smart getting values from python dictionary""" + val = src + for key in path: + if not isinstance(val, dict): + return None + val = val.get(key) + if val is None: + return None + return val diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 9b4117ff8..3319a4fdf 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -36,7 +36,7 @@ from spl.token.instructions import get_associated_token_address, create_associated_token_account, transfer2, Transfer2Params from ..environment import neon_cli, evm_loader_id, ETH_TOKEN_MINT_ID, COLLATERAL_POOL_BASE, read_elf_params -from ..common.utils import get_from_dict +from ..common_neon.utils import get_from_dict from .eth_proto import Trx logger = logging.getLogger(__name__) diff --git a/proxy/testing/test_utils.py b/proxy/testing/test_utils.py index 4d09a3ee7..137ffbf01 100644 --- a/proxy/testing/test_utils.py +++ b/proxy/testing/test_utils.py @@ -1,5 +1,5 @@ import unittest -from ..common.utils import get_from_dict +from ..common_neon.utils import get_from_dict class TestUtils(unittest.TestCase): From 4995d3f98c2d59554389921b5c02c2db7ad5d682 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 08:56:18 +0500 Subject: [PATCH 40/95] Emphasize meaning of trx extending functions This reverts commit 505653656a5406d64592c4fa2ebcf8ff1aed8edd. # Conflicts: # proxy/testing/test_eth_sendRawTransaction.py --- proxy/plugin/solana_rest_api_tools.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 3319a4fdf..61acde0fa 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -953,7 +953,7 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - create_token_and_airdrop_trx(client, signer, EthereumAddress(address), trx, code_account) + extend_trx_with_create_and_airdrop(client, signer, EthereumAddress(address), trx, code_account) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1226,7 +1226,7 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_token_account: PublicKey, trx: Transaction): +def extend_trx_with_transfer(owner_account: SolanaAccount, dest_token_account: PublicKey, trx: Transaction): owner_sol_addr = owner_account.public_key() owner_token_addr = getTokenAddr(owner_sol_addr) transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, @@ -1241,16 +1241,16 @@ def add_airdrop_transfer_to_trx(owner_account: SolanaAccount, dest_token_account trx.add(transfer_instruction) -def create_token_and_airdrop_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, - trx: Transaction, code_acc=None): +def extend_trx_with_create_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, + trx: Transaction, code_acc=None): create_trx, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) trx.add(create_trx) - add_airdrop_transfer_to_trx(signer, token_address, trx) + extend_trx_with_transfer(signer, token_address, trx) def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): trx = Transaction() - create_token_and_airdrop_trx(client, signer, eth_acc, trx) + extend_trx_with_create_and_airdrop(client, signer, eth_acc, trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: From b61f5a8c92249d89e28bce5f5568bc0a03930835 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 09:41:22 +0500 Subject: [PATCH 41/95] Resolve @otselik remarks # Conflicts: # proxy/testing/test_eth_sendRawTransaction.py --- proxy/common_neon/__init__.py | 0 proxy/plugin/solana_rest_api_tools.py | 23 +++++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 proxy/common_neon/__init__.py diff --git a/proxy/common_neon/__init__.py b/proxy/common_neon/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 61acde0fa..9c334988c 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -953,7 +953,8 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - extend_trx_with_create_and_airdrop(client, signer, EthereumAddress(address), trx, code_account) + create_airdrop_trx = get_create_and_airdrop_trx(client, signer, EthereumAddress(address), code_account) + trx.add(create_airdrop_trx) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1105,7 +1106,7 @@ def call_signed_with_holder_acc(signer, client, eth_trx, perm_accs, trx_accs, st return call_continue(signer, client, perm_accs, trx_accs, steps, eth_trx) -def create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: +def get_create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) @@ -1226,7 +1227,7 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def extend_trx_with_transfer(owner_account: SolanaAccount, dest_token_account: PublicKey, trx: Transaction): +def get_transfer_instruction(owner_account: SolanaAccount, dest_token_account: PublicKey) -> TransactionInstruction: owner_sol_addr = owner_account.public_key() owner_token_addr = getTokenAddr(owner_sol_addr) transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, @@ -1238,19 +1239,21 @@ def extend_trx_with_transfer(owner_account: SolanaAccount, dest_token_account: P program_id=TOKEN_PROGRAM_ID)) logger.debug(f"Token transfer from token: {owner_token_addr}, owned by: {owner_sol_addr}, to token: " f"{dest_token_account}, owned by: {dest_token_account} , value: {NEW_USER_AIRDROP_AMOUNT}") - trx.add(transfer_instruction) + return transfer_instruction -def extend_trx_with_create_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, - trx: Transaction, code_acc=None): - create_trx, token_address = create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) +def get_create_and_airdrop_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, + code_acc=None) -> Transaction: + trx = Transaction() + create_trx, token_address = get_create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) trx.add(create_trx) - extend_trx_with_transfer(signer, token_address, trx) + transfer_instruction = get_transfer_instruction(signer, token_address) + trx.add(transfer_instruction) + return trx def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): - trx = Transaction() - extend_trx_with_create_and_airdrop(client, signer, eth_acc, trx) + trx = get_create_and_airdrop_trx(client, signer, eth_acc) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: From 0c047e7453bb1a3e56422dccd5448013be77084e Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 09:45:39 +0500 Subject: [PATCH 42/95] rollback common/utils.py --- proxy/common/utils.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/proxy/common/utils.py b/proxy/common/utils.py index 053aa92ca..ecdc4e9fe 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -208,15 +208,3 @@ def get_available_port() -> int: sock.bind(('', 0)) _, port = sock.getsockname() return int(port) - - -def get_from_dict(src: Dict, *path) -> Optional[Any]: - """Provides smart getting values from python dictionary""" - val = src - for key in path: - if not isinstance(val, dict): - return None - val = val.get(key) - if val is None: - return None - return val From e1fd2997ae09b7e5f89efc896133a719549e9239 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 10:05:32 +0500 Subject: [PATCH 43/95] Rollback some changes --- proxy/plugin/solana_rest_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 0e14f1ae7..5d71c23c7 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -63,9 +63,9 @@ def __init__(self): proxy_id_glob.value += 1 logger.debug("worker id {}".format(self.proxy_id)) - self.perm_accs = PermanentAccounts(self.client, self.signer, self.proxy_id) neon_config_load(self) + @staticmethod def get_solana_account() -> Optional[sol_Account]: solana_account: Optional[sol_Account] = None From 1c9862cea5eec014ed2ae3359b5ee1b14b989563 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 15 Nov 2021 19:31:31 +0500 Subject: [PATCH 44/95] Resolve remarks --- proxy/plugin/solana_rest_api.py | 2 +- proxy/plugin/solana_rest_api_tools.py | 148 +++++++++++++------------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 5d71c23c7..0ba353f58 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -357,7 +357,7 @@ def eth_getTransactionByHash(self, trxId, block_info = None): "s": eth_trx[8], } - logger.debug ("eth_getTransactionByHash: %s", json.dumps(ret, indent=3)) + logger.debug("eth_getTransactionByHash: %s", json.dumps(ret, indent=3)) return ret def eth_getCode(self, param, param1): diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index c748c88a1..d00229954 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -68,11 +68,6 @@ STORAGE_SIZE = 128 * 1024 - -class SolanaErrors(Enum): - AccountNotFound = "Invalid param: could not find account" - - ACCOUNT_INFO_LAYOUT = cStruct( "type" / Int8ul, "ether" / Bytes(20), @@ -609,6 +604,7 @@ def call_continue(signer, client, perm_accs, trx_info, steps): return sol_instr_21_cancel(signer, client, perm_accs, trx_info) + def call_continue_iterative(signer, client, perm_accs, trx_info, step_count): while True: logger.debug("Continue iterative step:") @@ -798,6 +794,7 @@ def update_transaction_cost(receipt, eth_trx, extra_sol_trx=False, reason=None): reason if reason else '' ) + def create_account_list_by_emulate(signer, client, eth_trx): sender_ether = bytes.fromhex(eth_trx.sender()) add_keys_05 = [] @@ -884,8 +881,7 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - create_airdrop_trx = get_create_and_airdrop_trx(client, signer, EthereumAddress(address), code_account) - trx.add(create_airdrop_trx) + extend_trx_with_create_and_airdrop(client, signer, EthereumAddress(address), code_account, trx=trx) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -982,6 +978,7 @@ def call_signed(signer, client, eth_trx, steps): finally: del perm_accs + def call_signed_iterative(signer, client, eth_trx, perm_accs, trx_info, steps, msg, create_acc_trx): if len(create_acc_trx.instructions): precall_txs = Transaction() @@ -1035,57 +1032,6 @@ def call_signed_with_holder_acc(signer, client, eth_trx, perm_accs, trx_info, st return call_continue(signer, client, perm_accs, trx_info, steps) -def get_create_eth_account_trx(client: SolanaClient, signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) -> Tuple[Transaction, PublicKey]: - - solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) - token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) - logger.debug(f'Create eth account: {eth_address}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') - - sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) - value = get_from_dict(sender_sol_info, "result", "value") - if value is not None: - logger.error(f"Failed to create eth account: {eth_address}, associated: {token_acc_address}, already exists") - raise Exception("Account already exists") - - base = signer.public_key() - - data = bytes.fromhex('02000000') + CREATE_ACCOUNT_LAYOUT.build(dict(lamports=0, - space=0, - ether=bytes(eth_address), - nonce=nonce)) - trx = Transaction() - if code_acc is None: - trx.add(TransactionInstruction( - program_id=evm_loader_id, - data=data, - keys=[ - AccountMeta(pubkey=base, is_signer=True, is_writable=True), - AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), - AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), - AccountMeta(pubkey=system, is_signer=False, is_writable=False), - AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), - ])) - else: - trx.add(TransactionInstruction( - program_id=evm_loader_id, - data=data, - keys=[ - AccountMeta(pubkey=base, is_signer=True, is_writable=True), - AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), - AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), - AccountMeta(pubkey=PublicKey(code_acc), is_signer=False, is_writable=True), - AccountMeta(pubkey=system, is_signer=False, is_writable=False), - AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), - AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), - ])) - return trx, token_acc_address - - def createERC20TokenAccountTrx(signer, token_info): trx = Transaction() trx.add(TransactionInstruction( @@ -1105,7 +1051,6 @@ def createERC20TokenAccountTrx(signer, token_info): return trx - def write_trx_to_holder_account(signer, client, holder, acc_id, eth_trx): msg = eth_trx.signature() + len(eth_trx.unsigned_msg()).to_bytes(8, byteorder="little") + eth_trx.unsigned_msg() @@ -1156,7 +1101,58 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def get_transfer_instruction(owner_account: SolanaAccount, dest_token_account: PublicKey) -> TransactionInstruction: +def is_account_exists(client: SolanaClient, solana_address: str) -> bool: + sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) + value = get_from_dict(sender_sol_info, "result", "value") + return value is not None + + +def make_create_eth_account_trx(signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) \ + -> Tuple[Transaction, PublicKey]: + + solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) + token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) + logger.debug(f'Create eth account: {eth_address}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') + + base = signer.public_key() + data = bytes.fromhex('02000000') + CREATE_ACCOUNT_LAYOUT.build(dict(lamports=0, + space=0, + ether=bytes(eth_address), + nonce=nonce)) + trx = Transaction() + if code_acc is None: + trx.add(TransactionInstruction( + program_id=evm_loader_id, + data=data, + keys=[ + AccountMeta(pubkey=base, is_signer=True, is_writable=True), + AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), + AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), + AccountMeta(pubkey=system, is_signer=False, is_writable=False), + AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), + ])) + else: + trx.add(TransactionInstruction( + program_id=evm_loader_id, + data=data, + keys=[ + AccountMeta(pubkey=base, is_signer=True, is_writable=True), + AccountMeta(pubkey=PublicKey(solana_address), is_signer=False, is_writable=True), + AccountMeta(pubkey=token_acc_address, is_signer=False, is_writable=True), + AccountMeta(pubkey=PublicKey(code_acc), is_signer=False, is_writable=True), + AccountMeta(pubkey=system, is_signer=False, is_writable=False), + AccountMeta(pubkey=ETH_TOKEN_MINT_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False), + AccountMeta(pubkey=rentid, is_signer=False, is_writable=False), + ])) + return trx, token_acc_address + + +def make_transfer_instruction(owner_account: SolanaAccount, dest_token_account: PublicKey) -> TransactionInstruction: owner_sol_addr = owner_account.public_key() owner_token_addr = getTokenAddr(owner_sol_addr) transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, @@ -1171,18 +1167,26 @@ def get_transfer_instruction(owner_account: SolanaAccount, dest_token_account: P return transfer_instruction -def get_create_and_airdrop_trx(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, - code_acc=None) -> Transaction: - trx = Transaction() - create_trx, token_address = get_create_eth_account_trx(client, signer, eth_acc, evm_loader_id, code_acc) +def extend_trx_with_create_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, + code_acc=None, *, trx) -> bool: + if NEW_USER_AIRDROP_AMOUNT <= 0: + return False + + solana_address, nonce = ether2program(eth_acc, evm_loader_id, signer.public_key()) + if is_account_exists(client, solana_address): + return False + + create_trx, token_address = make_create_eth_account_trx(signer, eth_acc, evm_loader_id, code_acc) trx.add(create_trx) - transfer_instruction = get_transfer_instruction(signer, token_address) + transfer_instruction = make_transfer_instruction(signer, token_address) trx.add(transfer_instruction) - return trx + return True def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): - trx = get_create_and_airdrop_trx(client, signer, eth_acc) + trx = Transaction() + if not extend_trx_with_create_and_airdrop(client, signer, eth_acc, trx=trx): + return result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: @@ -1209,17 +1213,13 @@ def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, ev account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, signer.public_key()) logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") + if not is_account_exists(client, account): + create_token_and_airdrop(client, signer, eth_acc) + balance, error = get_token_balance_gwei(client, account, eth_acc) if error is None: return int(balance) - if error.get("message") == SolanaErrors.AccountNotFound.value and NEW_USER_AIRDROP_AMOUNT > 0: - logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - create_token_and_airdrop(client, signer, eth_acc) - balance, error = get_token_balance_gwei(client, account, eth_acc) - if error is None: - return int(balance) - logger.error(f"Failed to get balance for account: {eth_acc}, error occurred: {error}") raise Exception("Getting balance error") From 0bbc7d9b2c68bb70221b70939be5850120f2b676 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 16 Nov 2021 09:24:34 +0500 Subject: [PATCH 45/95] Use exception to check result of get_token_balance_gwei --- proxy/plugin/solana_rest_api_tools.py | 47 +++++++++++-------- .../testing/test_airdropping_eth_accounts.py | 3 +- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index d00229954..a6c40bb12 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -45,6 +45,14 @@ logger.setLevel(logging.DEBUG) +class SolanaErrors(Enum): + AccountNotFound = "Invalid param: could not find account" + + +class SolanaAccountNotFound(Exception): + """Provides special error processing""" + + NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) location_bin = ".deploy_contract.bin" confirmation_check_delay = float(os.environ.get("NEON_CONFIRMATION_CHECK_DELAY", "0.1")) @@ -1194,18 +1202,22 @@ def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_ac raise Exception("Create account error") -def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str, eth_acc: EthereumAddress) \ - -> [Optional[int], Optional[Union[Dict, str]]]: - token_account = get_associated_token_address(PublicKey(token_owner_acc), ETH_TOKEN_MINT_ID) - rpc_response = client.get_token_account_balance(token_account, commitment=Confirmed) +def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str, eth_acc: EthereumAddress) -> int: + token_acc = get_associated_token_address(PublicKey(token_owner_acc), ETH_TOKEN_MINT_ID) + rpc_response = client.get_token_account_balance(token_acc, commitment=Confirmed) error = rpc_response.get('error') - if error is None: - balance = get_from_dict(rpc_response, "result", "value", "amount") - if balance is None: - return None, f"Failed to get token balance from: {rpc_response}, by eth account:" \ - f" {eth_acc} aka: {token_account} at token: {ETH_TOKEN_MINT_ID}" - return int(balance), None - return None, error + if error is not None: + message = error.get("message") + if message == SolanaErrors.AccountNotFound.value: + raise SolanaAccountNotFound(message) + logger.error(f"Failed to get_token_balance_gwei, got get_token_account_balance error: \"{message}\"") + raise Exception("Getting balance error") + + balance = get_from_dict(rpc_response, "result", "value", "amount") + if balance is None: + logger.error(f"Failed to get_token_balance_gwei from: {rpc_response}, eth account: {eth_acc} aka: {token_acc}") + raise Exception("Unexpected token balance response") + return int(balance) def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress) -> int: @@ -1213,15 +1225,12 @@ def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, ev account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, signer.public_key()) logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - if not is_account_exists(client, account): + try: + return get_token_balance_gwei(client, account, eth_acc) + except SolanaAccountNotFound: + logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") create_token_and_airdrop(client, signer, eth_acc) - - balance, error = get_token_balance_gwei(client, account, eth_acc) - if error is None: - return int(balance) - - logger.error(f"Failed to get balance for account: {eth_acc}, error occurred: {error}") - raise Exception("Getting balance error") + return get_token_balance_gwei(client, account, eth_acc) def getTokenAddr(account): diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 43850aaa7..8397e4402 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -67,8 +67,7 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str def _get_balance_wei(self, eth_acc: str) -> int: pub_key = self._host_solana_account.public_key() token_owner_account, nonce = ether2program(eth_acc, self._EVM_LOADER_ID, pub_key) - balance, error = get_token_balance_gwei(self._solana_client, token_owner_account, EthereumAddress(eth_acc)) - self.assertIsNone(error) + balance = get_token_balance_gwei(self._solana_client, token_owner_account, EthereumAddress(eth_acc)) self.assertIsNotNone(balance) self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei From f1dc4f71f3f1a3e786c612ab17d8f27949a917c9 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 16 Nov 2021 09:49:00 +0500 Subject: [PATCH 46/95] just not to loose changes --- proxy/plugin/solana_rest_api_tools.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index a6c40bb12..b0d911b76 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -889,7 +889,8 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - extend_trx_with_create_and_airdrop(client, signer, EthereumAddress(address), code_account, trx=trx) + if is_airdrop_allowed(client, signer, EthereumAddress(address)): + extend_trx_with_create_and_airdrop(signer, EthereumAddress(address), code_account, trx=trx) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1175,26 +1176,28 @@ def make_transfer_instruction(owner_account: SolanaAccount, dest_token_account: return transfer_instruction -def extend_trx_with_create_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress, - code_acc=None, *, trx) -> bool: +def is_airdrop_allowed(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): if NEW_USER_AIRDROP_AMOUNT <= 0: return False solana_address, nonce = ether2program(eth_acc, evm_loader_id, signer.public_key()) if is_account_exists(client, solana_address): return False + return True + +def extend_trx_with_create_and_airdrop(signer: SolanaAccount, eth_acc: EthereumAddress, code_acc=None, *, trx): create_trx, token_address = make_create_eth_account_trx(signer, eth_acc, evm_loader_id, code_acc) trx.add(create_trx) transfer_instruction = make_transfer_instruction(signer, token_address) trx.add(transfer_instruction) - return True def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): - trx = Transaction() - if not extend_trx_with_create_and_airdrop(client, signer, eth_acc, trx=trx): + if not is_airdrop_allowed(client, signer, eth_acc): return + trx = Transaction() + extend_trx_with_create_and_airdrop(signer, eth_acc, trx=trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: From 781e986392b086cff0853122ab09e78295b7f61a Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 16 Nov 2021 09:58:10 +0500 Subject: [PATCH 47/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index b0d911b76..a159dd166 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1194,8 +1194,6 @@ def extend_trx_with_create_and_airdrop(signer: SolanaAccount, eth_acc: EthereumA def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): - if not is_airdrop_allowed(client, signer, eth_acc): - return trx = Transaction() extend_trx_with_create_and_airdrop(signer, eth_acc, trx=trx) result = send_transaction(client, trx, signer) @@ -1231,9 +1229,11 @@ def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, ev try: return get_token_balance_gwei(client, account, eth_acc) except SolanaAccountNotFound: - logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") - create_token_and_airdrop(client, signer, eth_acc) - return get_token_balance_gwei(client, account, eth_acc) + logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID} - create if allowed") + if is_airdrop_allowed(client, signer, eth_acc): + create_token_and_airdrop(client, signer, eth_acc) + return get_token_balance_gwei(client, account, eth_acc) + raise def getTokenAddr(account): From 2f9216cb72996f4f74e4cd867a7b0a1617147514 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 16 Nov 2021 18:47:30 +0500 Subject: [PATCH 48/95] Provide estimate_gas with airdropping --- proxy/plugin/solana_rest_api.py | 15 ++++++--------- proxy/plugin/solana_rest_api_tools.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 0ba353f58..08b81a43b 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -28,12 +28,11 @@ import threading from .solana_rest_api_tools import EthereumAddress, get_token_balance_or_airdrop, getAccountInfo, call_signed, \ - call_emulated, EthereumError, neon_config_load, MINIMAL_GAS_PRICE + call_emulated, EthereumError, neon_config_load, MINIMAL_GAS_PRICE, estimate_gas from solana.rpc.commitment import Commitment, Confirmed from web3 import Web3 import logging from ..core.acceptor.pool import proxy_id_glob -import os from ..indexer.utils import get_trx_results, LogDB from ..indexer.sql_dict import SQLDict from ..environment import evm_loader_id, solana_cli, solana_url @@ -45,7 +44,6 @@ modelInstanceLock = threading.Lock() modelInstance = None -EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "0")) class EthereumModel: def __init__(self): @@ -105,12 +103,11 @@ def eth_gasPrice(self): def eth_estimateGas(self, param): try: - caller_id = param['from'] if 'from' in param else "0x0000000000000000000000000000000000000000" - contract_id = param['to'] if 'to' in param else "deploy" - data = param['data'] if 'data' in param else "None" - value = param['value'] if 'value' in param else "" - result = call_emulated(contract_id, caller_id, data, value) - return result['used_gas']+EXTRA_GAS + caller_id = param.get('from', "0x0000000000000000000000000000000000000000") + contract_id = param.get('to', "deploy") + data = param.get('data', "None") + value = param.get('value', "") + return estimate_gas(self.client, self.signer, contract_id, EthereumAddress(caller_id), data, value) except Exception as err: logger.debug("Exception on eth_estimateGas: %s", err) raise diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index a159dd166..f61c76f66 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -108,6 +108,9 @@ class SolanaAccountNotFound(Exception): AccountMeta(pubkey=sysvarclock, is_signer=False, is_writable=False), ] +EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "0")) + + class SQLCost(): def __init__(self): @@ -1277,3 +1280,11 @@ def make_instruction_data_from_tx(instruction, private_key=None): return (pub.to_canonical_address(), sig.to_bytes(), raw_msg) else: raise Exception("function gets ") + + +def estimate_gas(client: SolanaClient, signer: SolanaAccount, contract_id: str, eth_caller_address: EthereumAddress, + data: str = None, value: str = None): + if is_airdrop_allowed(client, signer, eth_caller_address): + create_token_and_airdrop(client, signer, eth_caller_address) + result = call_emulated(contract_id, str(eth_caller_address), data, value) + return result['used_gas'] + EXTRA_GAS From 65bfb8d46b8b901f39c34c795fbd6976adeb6480 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 20:07:16 +0500 Subject: [PATCH 49/95] just not to loose changes --- proxy/testing/test_airdropping_eth_accounts.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 8397e4402..2f9dbe6df 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -50,6 +50,10 @@ def test_airdrop_onto_wrapped_new_address(self): self.assertEqual(self._EXPECTED_BALANCE_WEI, wrapper_actual_balance) self.assertEqual(self._EXPECTED_BALANCE_WEI, nested_actual_balance) + def test_raise_on_constructing(self): + contract_owner: LocalAccount = self._web3.eth.account.create() + contract = self._compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) + def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str) -> web3_eth.Contract: compiled_sol = solcx.compile_source(source) contract_id, contract_interface = compiled_sol.popitem() @@ -92,6 +96,7 @@ def _get_balance_wei(self, eth_acc: str) -> int: contract Wrapper { address private nested_address; constructor() { + reqire(False, "test value") Nested nested = new Nested(); nested_address = address(nested); } From 92f26c8c9082b1d7ff8d1a9ff98685a1ff6c8bd5 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 20:54:24 +0500 Subject: [PATCH 50/95] Update tests --- proxy/testing/test_airdropping_eth_accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 8397e4402..c03259ccf 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -67,7 +67,7 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str def _get_balance_wei(self, eth_acc: str) -> int: pub_key = self._host_solana_account.public_key() token_owner_account, nonce = ether2program(eth_acc, self._EVM_LOADER_ID, pub_key) - balance = get_token_balance_gwei(self._solana_client, token_owner_account, EthereumAddress(eth_acc)) + balance = get_token_balance_gwei(self._solana_client, token_owner_account) self.assertIsNotNone(balance) self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei From a62f41334d96bfd679a3172342625edafa844fdf Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 20:57:24 +0500 Subject: [PATCH 51/95] Simplify airdrop processing --- proxy/plugin/solana_rest_api_tools.py | 54 +++++++++++---------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index a159dd166..221cff335 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -889,8 +889,7 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - if is_airdrop_allowed(client, signer, EthereumAddress(address)): - extend_trx_with_create_and_airdrop(signer, EthereumAddress(address), code_account, trx=trx) + extend_trx_with_create_eth_account_and_airdrop(signer, EthereumAddress(address), code_account, trx=trx) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1176,34 +1175,26 @@ def make_transfer_instruction(owner_account: SolanaAccount, dest_token_account: return transfer_instruction -def is_airdrop_allowed(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): - if NEW_USER_AIRDROP_AMOUNT <= 0: - return False - - solana_address, nonce = ether2program(eth_acc, evm_loader_id, signer.public_key()) - if is_account_exists(client, solana_address): - return False - return True - - -def extend_trx_with_create_and_airdrop(signer: SolanaAccount, eth_acc: EthereumAddress, code_acc=None, *, trx): - create_trx, token_address = make_create_eth_account_trx(signer, eth_acc, evm_loader_id, code_acc) +def extend_trx_with_create_eth_account_and_airdrop(signer: SolanaAccount, eth_account: EthereumAddress, code_acc=None, *, trx): + create_trx, associated_token_account = make_create_eth_account_trx(signer, eth_account, evm_loader_id, code_acc) trx.add(create_trx) - transfer_instruction = make_transfer_instruction(signer, token_address) + if NEW_USER_AIRDROP_AMOUNT <= 0: + return + transfer_instruction = make_transfer_instruction(signer, associated_token_account) trx.add(transfer_instruction) -def create_token_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_acc: EthereumAddress): +def create_eth_account_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_account: EthereumAddress): trx = Transaction() - extend_trx_with_create_and_airdrop(signer, eth_acc, trx=trx) + extend_trx_with_create_eth_account_and_airdrop(signer, eth_account, trx=trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: - logger.error(f"Failed to create and mint token account: {eth_acc}, error occurred: {error}") - raise Exception("Create account error") + logger.error(f"Failed to create eth_account and airdrop: {eth_account}, error occurred: {error}") + raise Exception("Create eth_account error") -def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str, eth_acc: EthereumAddress) -> int: +def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str) -> int: token_acc = get_associated_token_address(PublicKey(token_owner_acc), ETH_TOKEN_MINT_ID) rpc_response = client.get_token_account_balance(token_acc, commitment=Confirmed) error = rpc_response.get('error') @@ -1211,29 +1202,28 @@ def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str, eth_acc: message = error.get("message") if message == SolanaErrors.AccountNotFound.value: raise SolanaAccountNotFound(message) - logger.error(f"Failed to get_token_balance_gwei, got get_token_account_balance error: \"{message}\"") + logger.error(f"Failed to get_token_balance_gwei byassociated_token_account: {token_acc}, " + f"got get_token_account_balance error: \"{message}\"") raise Exception("Getting balance error") balance = get_from_dict(rpc_response, "result", "value", "amount") if balance is None: - logger.error(f"Failed to get_token_balance_gwei from: {rpc_response}, eth account: {eth_acc} aka: {token_acc}") - raise Exception("Unexpected token balance response") + logger.error(f"Failed to get_token_balance_gwei by associated_token_account: {token_acc}, response: {rpc_response}") + raise Exception("Unexpected get_balance response") return int(balance) -def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_acc: EthereumAddress) -> int: +def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_account: EthereumAddress) -> int: - account, nonce = ether2program(bytes(eth_acc).hex(), evm_loader, signer.public_key()) - logger.debug(f"Get balance for eth account: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID}") + associated_token_account, nonce = ether2program(bytes(eth_account).hex(), evm_loader, signer.public_key()) + logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account} at token: {ETH_TOKEN_MINT_ID}") try: - return get_token_balance_gwei(client, account, eth_acc) + return get_token_balance_gwei(client, associated_token_account) except SolanaAccountNotFound: - logger.debug(f"Account not found: {eth_acc} aka: {account} at token: {ETH_TOKEN_MINT_ID} - create if allowed") - if is_airdrop_allowed(client, signer, eth_acc): - create_token_and_airdrop(client, signer, eth_acc) - return get_token_balance_gwei(client, account, eth_acc) - raise + logger.debug(f"Account not found: {eth_account} aka: {associated_token_account} - create") + create_eth_account_and_airdrop(client, signer, eth_account) + return get_token_balance_gwei(client, associated_token_account) def getTokenAddr(account): From 026a4a6e8ed8d55e07c079d0f863b52983fc474d Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 21:36:01 +0500 Subject: [PATCH 52/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 221cff335..aaf1e50c7 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1194,21 +1194,21 @@ def create_eth_account_and_airdrop(client: SolanaClient, signer: SolanaAccount, raise Exception("Create eth_account error") -def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str) -> int: - token_acc = get_associated_token_address(PublicKey(token_owner_acc), ETH_TOKEN_MINT_ID) - rpc_response = client.get_token_account_balance(token_acc, commitment=Confirmed) +def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: + associated_token_account = get_associated_token_address(PublicKey(pda_account), ETH_TOKEN_MINT_ID) + rpc_response = client.get_token_account_balance(associated_token_account, commitment=Confirmed) error = rpc_response.get('error') if error is not None: message = error.get("message") if message == SolanaErrors.AccountNotFound.value: raise SolanaAccountNotFound(message) - logger.error(f"Failed to get_token_balance_gwei byassociated_token_account: {token_acc}, " + logger.error(f"Failed to get_token_balance_gwei by associated_token_account: {associated_token_account}, " f"got get_token_account_balance error: \"{message}\"") raise Exception("Getting balance error") balance = get_from_dict(rpc_response, "result", "value", "amount") if balance is None: - logger.error(f"Failed to get_token_balance_gwei by associated_token_account: {token_acc}, response: {rpc_response}") + logger.error(f"Failed to get_token_balance_gwei by associated_token_account: {associated_token_account}, response: {rpc_response}") raise Exception("Unexpected get_balance response") return int(balance) @@ -1216,7 +1216,7 @@ def get_token_balance_gwei(client: SolanaClient, token_owner_acc: str) -> int: def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_account: EthereumAddress) -> int: associated_token_account, nonce = ether2program(bytes(eth_account).hex(), evm_loader, signer.public_key()) - logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account} at token: {ETH_TOKEN_MINT_ID}") + logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") try: return get_token_balance_gwei(client, associated_token_account) From db47b805cf76faacaa4a15a3f5e83d8162a11513 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 21:51:31 +0500 Subject: [PATCH 53/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index aaf1e50c7..2317f47dd 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1175,7 +1175,7 @@ def make_transfer_instruction(owner_account: SolanaAccount, dest_token_account: return transfer_instruction -def extend_trx_with_create_eth_account_and_airdrop(signer: SolanaAccount, eth_account: EthereumAddress, code_acc=None, *, trx): +def extend_trx_with_create_and_airdrop(signer: SolanaAccount, eth_account: EthereumAddress, code_acc=None, *, trx): create_trx, associated_token_account = make_create_eth_account_trx(signer, eth_account, evm_loader_id, code_acc) trx.add(create_trx) if NEW_USER_AIRDROP_AMOUNT <= 0: @@ -1186,7 +1186,7 @@ def extend_trx_with_create_eth_account_and_airdrop(signer: SolanaAccount, eth_ac def create_eth_account_and_airdrop(client: SolanaClient, signer: SolanaAccount, eth_account: EthereumAddress): trx = Transaction() - extend_trx_with_create_eth_account_and_airdrop(signer, eth_account, trx=trx) + extend_trx_with_create_and_airdrop(signer, eth_account, trx=trx) result = send_transaction(client, trx, signer) error = result.get("error") if error is not None: From a102251156bca30ff671b2fc7e7063b894d1a5b2 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 22:04:01 +0500 Subject: [PATCH 54/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 2317f47dd..8034bd057 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1160,18 +1160,18 @@ def make_create_eth_account_trx(signer: SolanaAccount, eth_address: EthereumAddr return trx, token_acc_address -def make_transfer_instruction(owner_account: SolanaAccount, dest_token_account: PublicKey) -> TransactionInstruction: - owner_sol_addr = owner_account.public_key() - owner_token_addr = getTokenAddr(owner_sol_addr) - transfer_instruction = transfer2(Transfer2Params(source=owner_token_addr, - owner=owner_sol_addr, - dest=dest_token_account, +def make_transfer_instruction(owner_pda_account: SolanaAccount, associated_token_account: PublicKey) -> TransactionInstruction: + owner_pda_address = owner_pda_account.public_key() + owner_associated_token_account = getTokenAddr(owner_pda_address) + transfer_instruction = transfer2(Transfer2Params(source=owner_associated_token_account, + owner=owner_pda_address, + dest=associated_token_account, amount=NEW_USER_AIRDROP_AMOUNT * eth_utils.denoms.gwei, decimals=9, mint=ETH_TOKEN_MINT_ID, program_id=TOKEN_PROGRAM_ID)) - logger.debug(f"Token transfer from token: {owner_token_addr}, owned by: {owner_sol_addr}, to token: " - f"{dest_token_account}, owned by: {dest_token_account} , value: {NEW_USER_AIRDROP_AMOUNT}") + logger.debug(f"Token transfer from token: {owner_associated_token_account}, owned by: {owner_pda_address}, to token: " + f"{associated_token_account}, owned by: {associated_token_account} , value: {NEW_USER_AIRDROP_AMOUNT}") return transfer_instruction From cc42eeaf92d122f4d258f9d1ae418f248c06ca2d Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 17 Nov 2021 22:23:55 +0500 Subject: [PATCH 55/95] Freeze changes up --- proxy/plugin/solana_rest_api_tools.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 8034bd057..8b44453f7 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -889,7 +889,7 @@ def create_account_list_by_emulate(signer, client, eth_trx): trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_size, PublicKey(evm_loader_id))) code_account_writable = acc_desc["writable"] - extend_trx_with_create_eth_account_and_airdrop(signer, EthereumAddress(address), code_account, trx=trx) + extend_trx_with_create_and_airdrop(signer, EthereumAddress(address), code_account, trx=trx) if address == to_address: contract_sol = PublicKey(acc_desc["account"]) @@ -1109,12 +1109,6 @@ def getLamports(client, evm_loader, eth_acc, base_account): return int(client.get_balance(account, commitment=Confirmed)['result']['value']) -def is_account_exists(client: SolanaClient, solana_address: str) -> bool: - sender_sol_info = client.get_account_info(solana_address, commitment=Confirmed) - value = get_from_dict(sender_sol_info, "result", "value") - return value is not None - - def make_create_eth_account_trx(signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) \ -> Tuple[Transaction, PublicKey]: From 458878e2ed5b2f4e2de56c022cfa07f6cb004ae1 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 18 Nov 2021 15:29:10 +0500 Subject: [PATCH 56/95] Isolate errors --- proxy/common_neon/errors.py | 11 +++++++++++ proxy/plugin/solana_rest_api_tools.py | 14 +++----------- 2 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 proxy/common_neon/errors.py diff --git a/proxy/common_neon/errors.py b/proxy/common_neon/errors.py new file mode 100644 index 000000000..e590afa64 --- /dev/null +++ b/proxy/common_neon/errors.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class SolanaErrors(Enum): + AccountNotFound = "Invalid param: could not find account" + + +class SolanaAccountNotFoundError(Exception): + """Provides special error processing""" + def __init__(self): + super().__init__(SolanaErrors.AccountNotFound.value) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 8b44453f7..4971250b6 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -10,7 +10,6 @@ from datetime import datetime from hashlib import sha256 from typing import NamedTuple, Optional, Union, Dict, Tuple -from enum import Enum import psycopg2 import rlp from base58 import b58decode, b58encode @@ -37,6 +36,7 @@ from ..environment import neon_cli, evm_loader_id, ETH_TOKEN_MINT_ID, COLLATERAL_POOL_BASE, read_elf_params from ..common_neon.utils import get_from_dict +from ..common_neon.errors import * from .eth_proto import Trx from ..core.acceptor.pool import new_acc_id_glob, acc_list_glob from ..indexer.sql_dict import POSTGRES_USER, POSTGRES_HOST, POSTGRES_DB, POSTGRES_PASSWORD @@ -45,14 +45,6 @@ logger.setLevel(logging.DEBUG) -class SolanaErrors(Enum): - AccountNotFound = "Invalid param: could not find account" - - -class SolanaAccountNotFound(Exception): - """Provides special error processing""" - - NEW_USER_AIRDROP_AMOUNT = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) location_bin = ".deploy_contract.bin" confirmation_check_delay = float(os.environ.get("NEON_CONFIRMATION_CHECK_DELAY", "0.1")) @@ -1195,7 +1187,7 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: if error is not None: message = error.get("message") if message == SolanaErrors.AccountNotFound.value: - raise SolanaAccountNotFound(message) + raise SolanaAccountNotFoundError() logger.error(f"Failed to get_token_balance_gwei by associated_token_account: {associated_token_account}, " f"got get_token_account_balance error: \"{message}\"") raise Exception("Getting balance error") @@ -1214,7 +1206,7 @@ def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, ev try: return get_token_balance_gwei(client, associated_token_account) - except SolanaAccountNotFound: + except SolanaAccountNotFoundError: logger.debug(f"Account not found: {eth_account} aka: {associated_token_account} - create") create_eth_account_and_airdrop(client, signer, eth_account) return get_token_balance_gwei(client, associated_token_account) From 8190225ca5370eae37b23e9b0788a2aad173d2f9 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 18 Nov 2021 15:39:26 +0500 Subject: [PATCH 57/95] spit and polish --- proxy/plugin/solana_rest_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index 0ba353f58..82ed9c755 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -12,6 +12,7 @@ import copy import json import unittest +import eth_utils import rlp import solana from solana.account import Account as sol_Account @@ -140,7 +141,7 @@ def eth_getBalance(self, account, tag): logger.debug('eth_getBalance: %s %s', account, eth_acc) balance = get_token_balance_or_airdrop(self.client, self.signer, evm_loader_id, eth_acc) - return hex(balance*10**9) + return hex(balance * eth_utils.denoms.gwei) def eth_getLogs(self, obj): fromBlock = None From f761ab6815cb887e7a3414ab839427da41804cb6 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 18 Nov 2021 15:43:58 +0500 Subject: [PATCH 58/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 4971250b6..42752a6d7 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1200,7 +1200,6 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_account: EthereumAddress) -> int: - associated_token_account, nonce = ether2program(bytes(eth_account).hex(), evm_loader, signer.public_key()) logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") From dfea3716f55cf394c473ee076c8e32b2163c1ff4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 18 Nov 2021 16:57:50 +0500 Subject: [PATCH 59/95] fix estimate gas --- proxy/plugin/solana_rest_api_tools.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index d1feb4bab..fa22f495e 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1272,9 +1272,15 @@ def make_instruction_data_from_tx(instruction, private_key=None): raise Exception("function gets ") +def is_account_exists(client: SolanaClient, pda_account: str) -> bool: + info = client.get_account_info(pda_account, commitment=Confirmed) + value = get_from_dict(info, "result", "value") + return value is not None + + def estimate_gas(client: SolanaClient, signer: SolanaAccount, contract_id: str, eth_caller_address: EthereumAddress, data: str = None, value: str = None): - if is_airdrop_allowed(client, signer, eth_caller_address): - create_token_and_airdrop(client, signer, eth_caller_address) + if is_account_exists(client, signer, eth_caller_address): + create_eth_account_and_airdrop(client, signer, eth_caller_address) result = call_emulated(contract_id, str(eth_caller_address), data, value) return result['used_gas'] + EXTRA_GAS From eb0bb4681efbf84fbfd9baa26abe615531ca058a Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 18 Nov 2021 18:16:56 +0500 Subject: [PATCH 60/95] not to loose changes --- proxy/indexer/utils.py | 2 +- proxy/plugin/solana_rest_api_tools.py | 13 +++++++------ proxy/testing/test_airdropping_eth_accounts.py | 13 +++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/proxy/indexer/utils.py b/proxy/indexer/utils.py index 7b45f725e..49d129795 100644 --- a/proxy/indexer/utils.py +++ b/proxy/indexer/utils.py @@ -175,7 +175,7 @@ class LogDB: def __init__(self): POSTGRES_DB = os.environ.get("POSTGRES_DB", "neon-db") POSTGRES_USER = os.environ.get("POSTGRES_USER", "neon-proxy") - POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "neon-proxy") + POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "neon-proxy-pass") POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "localhost") self.conn = psycopg2.connect( diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 3f318b552..41ef4776c 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -429,7 +429,7 @@ def solana2ether(public_key): return bytes(Web3.keccak(bytes.fromhex(public_key))[-20:]) -def ether2program(ether, program_id, base): +def ether2program(ether): if isinstance(ether, str): pass elif isinstance(ether, EthereumAddress): @@ -1094,20 +1094,20 @@ def _getAccountData(client, account, expected_length, owner=None): def getAccountInfo(client, eth_acc, base_account): - (account_sol, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader_id, base_account) + (account_sol, nonce) = ether2program(bytes(eth_acc).hex()) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) def getLamports(client, evm_loader, eth_acc, base_account): - (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) + (account, nonce) = ether2program(bytes(eth_acc).hex()) return int(client.get_balance(account, commitment=Confirmed)['result']['value']) def make_create_eth_account_trx(signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) \ -> Tuple[Transaction, PublicKey]: - solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) + solana_address, nonce = ether2program(eth_address) token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) logger.debug(f'Create eth account: {eth_address}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') @@ -1203,7 +1203,7 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_account: EthereumAddress) -> int: - associated_token_account, nonce = ether2program(bytes(eth_account).hex(), evm_loader, signer.public_key()) + associated_token_account, nonce = ether2program(bytes(eth_account).hex()) logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") try: @@ -1265,7 +1265,8 @@ def is_account_exists(client: SolanaClient, pda_account: str) -> bool: def estimate_gas(client: SolanaClient, signer: SolanaAccount, contract_id: str, eth_caller_address: EthereumAddress, data: str = None, value: str = None): - if is_account_exists(client, signer, eth_caller_address): + pda_account, nonce = ether2program(eth_caller_address) + if not is_account_exists(client, pda_account): create_eth_account_and_airdrop(client, signer, eth_caller_address) result = call_emulated(contract_id, str(eth_caller_address), data, value) return result['used_gas'] + EXTRA_GAS diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 118f0acf2..15216e4af 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -70,12 +70,22 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str def _get_balance_wei(self, eth_acc: str) -> int: pub_key = self._host_solana_account.public_key() - token_owner_account, nonce = ether2program(eth_acc, self._EVM_LOADER_ID, pub_key) + token_owner_account, nonce = ether2program(eth_acc) balance = get_token_balance_gwei(self._solana_client, token_owner_account) self.assertIsNotNone(balance) self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei + _CONTRACT_REQUIRES_LIST = ''' + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; + contract RequiresList { + constructor(address[] memory payees) { + require(payees.length > 0, "PaymentSplitter: no payees"); + } + } + ''' + _CONTRACT_STORAGE_SOURCE = ''' // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; @@ -96,7 +106,6 @@ def _get_balance_wei(self, eth_acc: str) -> int: contract Wrapper { address private nested_address; constructor() { - reqire(False, "test value") Nested nested = new Nested(); nested_address = address(nested); } From 73a0423a06dc0163da16c1fa48d69dce97bc613b Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 09:55:08 +0500 Subject: [PATCH 61/95] Remove extra args from ether2program --- proxy/plugin/solana_rest_api.py | 4 ++-- proxy/plugin/solana_rest_api_tools.py | 18 +++++++++--------- proxy/testing/test_airdropping_eth_accounts.py | 3 +-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index ef13a4748..53f7d217a 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -138,7 +138,7 @@ def eth_getBalance(self, account, tag): """ eth_acc = EthereumAddress(account) logger.debug('eth_getBalance: %s %s', account, eth_acc) - balance = get_token_balance_or_airdrop(self.client, self.signer, evm_loader_id, eth_acc) + balance = get_token_balance_or_airdrop(self.client, self.signer, eth_acc) return hex(balance * eth_utils.denoms.gwei) @@ -272,7 +272,7 @@ def eth_call(self, obj, tag): def eth_getTransactionCount(self, account, tag): logger.debug('eth_getTransactionCount: %s', account) try: - acc_info = getAccountInfo(self.client, EthereumAddress(account), self.signer.public_key()) + acc_info = getAccountInfo(self.client, EthereumAddress(account)) return hex(int.from_bytes(acc_info.trx_count, 'little')) except Exception as err: print("Can't get account info: %s"%err) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 42752a6d7..a001f25e5 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -426,7 +426,7 @@ def solana2ether(public_key): return bytes(Web3.keccak(bytes.fromhex(public_key))[-20:]) -def ether2program(ether, program_id, base): +def ether2program(ether): if isinstance(ether, str): pass elif isinstance(ether, EthereumAddress): @@ -1090,21 +1090,21 @@ def _getAccountData(client, account, expected_length, owner=None): return data -def getAccountInfo(client, eth_acc, base_account): - (account_sol, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader_id, base_account) +def getAccountInfo(client, eth_acc): + (account_sol, nonce) = ether2program(bytes(eth_acc).hex()) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) -def getLamports(client, evm_loader, eth_acc, base_account): - (account, nonce) = ether2program(bytes(eth_acc).hex(), evm_loader, base_account) - return int(client.get_balance(account, commitment=Confirmed)['result']['value']) +def getLamports(client, eth_acc): + pda_account, nonce = ether2program(bytes(eth_acc).hex()) + return int(client.get_balance(pda_account, commitment=Confirmed)['result']['value']) def make_create_eth_account_trx(signer: SolanaAccount, eth_address: EthereumAddress, evm_loader_id, code_acc=None) \ -> Tuple[Transaction, PublicKey]: - solana_address, nonce = ether2program(eth_address, evm_loader_id, signer.public_key()) + solana_address, nonce = ether2program(eth_address) token_acc_address = get_associated_token_address(PublicKey(solana_address), ETH_TOKEN_MINT_ID) logger.debug(f'Create eth account: {eth_address}, sol account: {solana_address}, token_acc_address: {token_acc_address}, nonce: {nonce}') @@ -1199,8 +1199,8 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: return int(balance) -def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, evm_loader: str, eth_account: EthereumAddress) -> int: - associated_token_account, nonce = ether2program(bytes(eth_account).hex(), evm_loader, signer.public_key()) +def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, eth_account: EthereumAddress) -> int: + associated_token_account, nonce = ether2program(bytes(eth_account).hex()) logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") try: diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index c03259ccf..ea9692586 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -65,8 +65,7 @@ def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str return contract def _get_balance_wei(self, eth_acc: str) -> int: - pub_key = self._host_solana_account.public_key() - token_owner_account, nonce = ether2program(eth_acc, self._EVM_LOADER_ID, pub_key) + token_owner_account, nonce = ether2program(eth_acc) balance = get_token_balance_gwei(self._solana_client, token_owner_account) self.assertIsNotNone(balance) self.assertIsInstance(balance, int) From 9e6b7dcf9c6b38a36586b7f151f0e053eebe7fe8 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 10:02:29 +0500 Subject: [PATCH 62/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index a001f25e5..41967c361 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1091,7 +1091,7 @@ def _getAccountData(client, account, expected_length, owner=None): def getAccountInfo(client, eth_acc): - (account_sol, nonce) = ether2program(bytes(eth_acc).hex()) + account_sol, nonce = ether2program(bytes(eth_acc).hex()) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) From 5be978cbfc558fa0947161b63e2caf2d444ec292 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 10:04:54 +0500 Subject: [PATCH 63/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 41967c361..e9d2436ae 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1091,13 +1091,13 @@ def _getAccountData(client, account, expected_length, owner=None): def getAccountInfo(client, eth_acc): - account_sol, nonce = ether2program(bytes(eth_acc).hex()) + account_sol, nonce = ether2program(str(eth_acc)) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) def getLamports(client, eth_acc): - pda_account, nonce = ether2program(bytes(eth_acc).hex()) + pda_account, nonce = ether2program(str(eth_acc)) return int(client.get_balance(pda_account, commitment=Confirmed)['result']['value']) @@ -1200,7 +1200,7 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, eth_account: EthereumAddress) -> int: - associated_token_account, nonce = ether2program(bytes(eth_account).hex()) + associated_token_account, nonce = ether2program(str(eth_account)) logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") try: From 884ec0f1c3d60d97642dbac0bc5a12fa66769247 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 10:09:02 +0500 Subject: [PATCH 64/95] spit and polish --- proxy/plugin/solana_rest_api_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index e9d2436ae..d7148b8fb 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1091,13 +1091,13 @@ def _getAccountData(client, account, expected_length, owner=None): def getAccountInfo(client, eth_acc): - account_sol, nonce = ether2program(str(eth_acc)) + account_sol, nonce = ether2program(eth_acc) info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) return AccountInfo.frombytes(info) def getLamports(client, eth_acc): - pda_account, nonce = ether2program(str(eth_acc)) + pda_account, nonce = ether2program(eth_acc) return int(client.get_balance(pda_account, commitment=Confirmed)['result']['value']) @@ -1200,7 +1200,7 @@ def get_token_balance_gwei(client: SolanaClient, pda_account: str) -> int: def get_token_balance_or_airdrop(client: SolanaClient, signer: SolanaAccount, eth_account: EthereumAddress) -> int: - associated_token_account, nonce = ether2program(str(eth_account)) + associated_token_account, nonce = ether2program(eth_account) logger.debug(f"Get balance for eth account: {eth_account} aka: {associated_token_account}") try: From a0e3ebb1eb0d32d144698eb3b3c8b802938979b4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 10:15:21 +0500 Subject: [PATCH 65/95] Spit and polish --- proxy/plugin/solana_rest_api_tools.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index d7148b8fb..2d76461ba 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -228,6 +228,7 @@ def write_holder_layout(nonce, offset, data): len(data).to_bytes(8, byteorder='little')+ data) + def get_account_info(client, storage_account): opts = { "encoding": "base64", @@ -1079,25 +1080,8 @@ def write_trx_to_holder_account(signer, client, holder, acc_id, eth_trx): logger.debug("confirmed: %s", rcpt) -def _getAccountData(client, account, expected_length, owner=None): - info = client.get_account_info(account, commitment=Confirmed)['result']['value'] - if info is None: - raise Exception("Can't get information about {}".format(account)) - - data = base64.b64decode(info['data'][0]) - if len(data) < expected_length: - raise Exception("Wrong data length for account data {}".format(account)) - return data - - -def getAccountInfo(client, eth_acc): - account_sol, nonce = ether2program(eth_acc) - info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) - return AccountInfo.frombytes(info) - - -def getLamports(client, eth_acc): - pda_account, nonce = ether2program(eth_acc) +def getLamports(client, eth_account): + pda_account, nonce = ether2program(eth_account) return int(client.get_balance(pda_account, commitment=Confirmed)['result']['value']) From 210d0523f230a868bffcd8113312186072006f58 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Fri, 19 Nov 2021 10:17:34 +0500 Subject: [PATCH 66/95] Rollback changes --- proxy/plugin/solana_rest_api_tools.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 2d76461ba..e20fdc923 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -1080,6 +1080,23 @@ def write_trx_to_holder_account(signer, client, holder, acc_id, eth_trx): logger.debug("confirmed: %s", rcpt) +def _getAccountData(client, account, expected_length, owner=None): + info = client.get_account_info(account, commitment=Confirmed)['result']['value'] + if info is None: + raise Exception("Can't get information about {}".format(account)) + + data = base64.b64decode(info['data'][0]) + if len(data) < expected_length: + raise Exception("Wrong data length for account data {}".format(account)) + return data + + +def getAccountInfo(client, eth_account: EthereumAddress): + account_sol, nonce = ether2program(eth_account) + info = _getAccountData(client, account_sol, ACCOUNT_INFO_LAYOUT.sizeof()) + return AccountInfo.frombytes(info) + + def getLamports(client, eth_account): pda_account, nonce = ether2program(eth_account) return int(client.get_balance(pda_account, commitment=Confirmed)['result']['value']) From 72ef3d0a8c588fb95c61a3513f4a09a97efe96bf Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 25 Nov 2021 14:25:57 +0500 Subject: [PATCH 67/95] Implement test --- .../testing/test_airdropping_eth_accounts.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index dbdefce9a..fda2fac04 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -7,7 +7,7 @@ import eth_utils from eth_account.account import LocalAccount -from web3 import Web3, eth as web3_eth +from web3 import Web3, eth as web3_eth, exceptions as web3_exceptions from solana.rpc.api import Client as SolanaClient from ..plugin.solana_rest_api import EthereumModel @@ -54,6 +54,16 @@ def test_raise_on_constructing(self): contract_owner: LocalAccount = self._web3.eth.account.create() contract = self._compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) + def test_eth_call_array_constructable_contract(self): + compile_result = solcx.compile_source(self._CONTRACT_LIST_CONSTRUCTABLE) + _, contract_interface = compile_result.popitem() + bytecode = contract_interface.get("bin") + abi = contract_interface.get("abi") + arr_constructable = self._web3.eth.contract(abi=abi, bytecode=bytecode) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + arr_constructable.constructor([]).buildTransaction() + self.assertEqual("ListConstructable: empty list", str(cm.exception)) + def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str) -> web3_eth.Contract: compiled_sol = solcx.compile_source(source) contract_id, contract_interface = compiled_sol.popitem() @@ -75,6 +85,16 @@ def _get_balance_wei(self, eth_acc: str) -> int: self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei + _CONTRACT_LIST_CONSTRUCTABLE = ''' + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; + contract ArrConstructable { + constructor(uint256[] memory vector_) payable { + require(vector_.length > 0, "ListConstructable: empty list"); + } + } + ''' + _CONTRACT_STORAGE_SOURCE = ''' // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; From e74088300442f270a805d34eee04d0ee32ea98a9 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 25 Nov 2021 18:47:10 +0500 Subject: [PATCH 68/95] Spit and polish --- .../testing/test_airdropping_eth_accounts.py | 28 ++++--------------- proxy/testing/testing_helpers.py | 24 ++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) create mode 100644 proxy/testing/testing_helpers.py diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 88168bce7..4a2fea330 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -5,29 +5,27 @@ import eth_account import eth_typing import eth_utils -from eth_account.account import LocalAccount -from web3 import Web3, eth as web3_eth, exceptions as web3_exceptions +from eth_account.account import LocalAccount +from web3 import Web3, exceptions as web3_exceptions from solana.rpc.api import Client as SolanaClient -from ..plugin.solana_rest_api import EthereumModel from ..plugin.solana_rest_api_tools import get_token_balance_gwei, ether2program +from .testing_helpers import compile_and_deploy_contract + class TestAirdroppingEthAccounts(unittest.TestCase): @classmethod def setUpClass(cls) -> None: - cls._EVM_LOADER_ID = os.environ.get("EVM_LOADER") new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') - cls._MINIMAL_GAS_PRICE = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") cls._solana_client = SolanaClient(solana_url) - cls._host_solana_account = EthereumModel.get_solana_account() def test_airdrop_on_get_balance(self): account: LocalAccount = eth_account.account.Account.create() @@ -37,13 +35,13 @@ def test_airdrop_on_get_balance(self): def test_airdrop_on_deploy(self): contract_owner: LocalAccount = self._web3.eth.account.create() - contract = self._compile_and_deploy_contract(contract_owner, self._CONTRACT_STORAGE_SOURCE) + contract = compile_and_deploy_contract(contract_owner, self._CONTRACT_STORAGE_SOURCE) actual_balance_wei = self._get_balance_wei(contract.address) self.assertEqual(self._EXPECTED_BALANCE_WEI, actual_balance_wei) def test_airdrop_onto_wrapped_new_address(self): contract_owner: LocalAccount = self._web3.eth.account.create() - contract = self._compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) + contract = compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) nested_contract_address = contract.functions.getNested().call() nested_actual_balance = self._get_balance_wei(nested_contract_address) wrapper_actual_balance = self._get_balance_wei(contract.address) @@ -70,20 +68,6 @@ def test_eth_call_array_constructable_contract(self): arr_constructable.constructor([]).buildTransaction() self.assertEqual("ListConstructable: empty list", str(cm.exception)) - def _compile_and_deploy_contract(self, contract_owner: LocalAccount, source: str) -> web3_eth.Contract: - compile_result = solcx.compile_source(source) - contract_id, contract_interface = compile_result.popitem() - contract = self._web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) - nonce = self._web3.eth.get_transaction_count(contract_owner.address) - chain_id = self._web3.eth.chain_id - trx_signed = self._web3.eth.account.sign_transaction( - dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=self._MINIMAL_GAS_PRICE, to='', value=0, data=contract.bytecode), - contract_owner.key) - trx_hash = self._web3.eth.send_raw_transaction(trx_signed.rawTransaction) - trx_receipt = self._web3.eth.wait_for_transaction_receipt(trx_hash) - contract = self._web3.eth.contract(address=trx_receipt.contractAddress, abi=contract.abi) - return contract - def _get_balance_wei(self, eth_account: str) -> int: token_owner_account, nonce = ether2program(eth_account) balance = get_token_balance_gwei(self._solana_client, token_owner_account) diff --git a/proxy/testing/testing_helpers.py b/proxy/testing/testing_helpers.py new file mode 100644 index 000000000..b5009ef6b --- /dev/null +++ b/proxy/testing/testing_helpers.py @@ -0,0 +1,24 @@ +import os +import solcx +from eth_account.account import LocalAccount +from web3 import Web3, eth as web3_eth +import eth_utils + + +def compile_and_deploy_contract(contract_owner: LocalAccount, solidity_source_code: str) -> web3_eth.Contract: + minimal_gas_price = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + web3 = Web3(Web3.HTTPProvider(proxy_url)) + + compile_result = solcx.compile_source(solidity_source_code) + contract_id, contract_interface = compile_result.popitem() + contract = web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + nonce = web3.eth.get_transaction_count(contract_owner.address) + chain_id = web3.eth.chain_id + trx_signed = web3.eth.account.sign_transaction( + dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=minimal_gas_price, to='', value=0, data=contract.bytecode), + contract_owner.key) + trx_hash = web3.eth.send_raw_transaction(trx_signed.rawTransaction) + trx_receipt = web3.eth.wait_for_transaction_receipt(trx_hash) + contract = web3.eth.contract(address=trx_receipt.contractAddress, abi=contract.abi) + return contract From 8a6595c013da2506c97679f5b020b93342ae2c63 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 25 Nov 2021 22:05:29 +0500 Subject: [PATCH 69/95] Just not to loose changes --- .../testing/test_airdropping_eth_accounts.py | 22 +---------- proxy/testing/test_contract_features.py | 38 +++++++++++++++++++ proxy/testing/testing_helpers.py | 12 +++++- 3 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 proxy/testing/test_contract_features.py diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 4a2fea330..026297ff5 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -7,11 +7,10 @@ import eth_utils from eth_account.account import LocalAccount -from web3 import Web3, exceptions as web3_exceptions +from web3 import Web3 from solana.rpc.api import Client as SolanaClient from ..plugin.solana_rest_api_tools import get_token_balance_gwei, ether2program - from .testing_helpers import compile_and_deploy_contract @@ -58,16 +57,6 @@ def test_airdrop_on_deploy_estimation(self): owner_balance = self._get_balance_wei(owner_eth_account.address) self.assertEqual(self._EXPECTED_BALANCE_WEI, owner_balance) - def test_eth_call_array_constructable_contract(self): - compile_result = solcx.compile_source(self._CONTRACT_LIST_CONSTRUCTABLE) - _, contract_interface = compile_result.popitem() - bytecode = contract_interface.get("bin") - abi = contract_interface.get("abi") - arr_constructable = self._web3.eth.contract(abi=abi, bytecode=bytecode) - with self.assertRaises(web3_exceptions.ContractLogicError) as cm: - arr_constructable.constructor([]).buildTransaction() - self.assertEqual("ListConstructable: empty list", str(cm.exception)) - def _get_balance_wei(self, eth_account: str) -> int: token_owner_account, nonce = ether2program(eth_account) balance = get_token_balance_gwei(self._solana_client, token_owner_account) @@ -75,15 +64,6 @@ def _get_balance_wei(self, eth_account: str) -> int: self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei - _CONTRACT_LIST_CONSTRUCTABLE = ''' - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0 <0.9.0; - contract ArrConstructable { - constructor(uint256[] memory vector_) payable { - require(vector_.length > 0, "ListConstructable: empty list"); - } - } - ''' _CONTRACT_STORAGE_SOURCE = ''' // SPDX-License-Identifier: GPL-3.0 diff --git a/proxy/testing/test_contract_features.py b/proxy/testing/test_contract_features.py new file mode 100644 index 000000000..e88f1d5f2 --- /dev/null +++ b/proxy/testing/test_contract_features.py @@ -0,0 +1,38 @@ +import unittest +import os + +import eth_utils + +from web3 import Web3, exceptions as web3_exceptions +from solana.rpc.api import Client as SolanaClient + +from .testing_helpers import compile_contract + + +class TestAirdroppingEthAccounts(unittest.TestCase): + + @classmethod + def setUpClass(cls) -> None: + new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) + cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') + + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) + solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") + cls._solana_client = SolanaClient(solana_url) + + def test_contract_raises_correct_error(self): + arr_constructable = compile_contract(self._CONTRACT_LIST_CONSTRUCTABLE) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + arr_constructable.constructor([]).buildTransaction() + self.assertEqual("ListConstructable: empty list", str(cm.exception)) + + _CONTRACT_LIST_CONSTRUCTABLE = ''' + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; + contract ArrConstructable { + constructor(uint256[] memory vector_) payable { + require(vector_.length > 0, "ListConstructable: empty list"); + } + } + ''' diff --git a/proxy/testing/testing_helpers.py b/proxy/testing/testing_helpers.py index b5009ef6b..f2a366121 100644 --- a/proxy/testing/testing_helpers.py +++ b/proxy/testing/testing_helpers.py @@ -5,16 +5,24 @@ import eth_utils -def compile_and_deploy_contract(contract_owner: LocalAccount, solidity_source_code: str) -> web3_eth.Contract: - minimal_gas_price = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei +def compile_contract(solidity_source_code: str): proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') web3 = Web3(Web3.HTTPProvider(proxy_url)) compile_result = solcx.compile_source(solidity_source_code) contract_id, contract_interface = compile_result.popitem() contract = web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + return contract + + +def compile_and_deploy_contract(contract_owner: LocalAccount, solidity_source_code: str) -> web3_eth.Contract: + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + web3 = Web3(Web3.HTTPProvider(proxy_url)) + + contract = compile_contract(solidity_source_code) nonce = web3.eth.get_transaction_count(contract_owner.address) chain_id = web3.eth.chain_id + minimal_gas_price = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei trx_signed = web3.eth.account.sign_transaction( dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=minimal_gas_price, to='', value=0, data=contract.bytecode), contract_owner.key) From a5cf3adcefce6d4f4d85e9c6c9592596c0d01077 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 8 Dec 2021 13:44:53 +0500 Subject: [PATCH 70/95] Implement revert processing --- proxy/plugin/solana_rest_api_tools.py | 2 +- .../testing/test_airdropping_eth_accounts.py | 19 +++-- proxy/testing/test_contract_features.py | 38 --------- proxy/testing/test_contract_reverting.py | 80 +++++++++++++++++++ proxy/testing/testing_helpers.py | 58 +++++++++----- 5 files changed, 127 insertions(+), 70 deletions(-) delete mode 100644 proxy/testing/test_contract_features.py create mode 100644 proxy/testing/test_contract_reverting.py diff --git a/proxy/plugin/solana_rest_api_tools.py b/proxy/plugin/solana_rest_api_tools.py index 26a98068c..d2996930d 100644 --- a/proxy/plugin/solana_rest_api_tools.py +++ b/proxy/plugin/solana_rest_api_tools.py @@ -487,7 +487,7 @@ def call_emulated(contract_id, caller_id, data=None, value=None): offset = int(result_value[8:8+64], 16) length = int(result_value[8+64:8+64+64], 16) message = str(bytes.fromhex(result_value[8+offset*2+64:8+offset*2+64+length*2]), 'utf8') - raise EthereumError(code=3, message='execution reverted: '+message, data='0x'+result_value) + raise EthereumError(code=3, message=message, data='0x'+result_value) if result["exit_status"] != "succeed": raise Exception("evm emulator error ", result) return result diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_eth_accounts.py index 026297ff5..a74117819 100644 --- a/proxy/testing/test_airdropping_eth_accounts.py +++ b/proxy/testing/test_airdropping_eth_accounts.py @@ -1,17 +1,16 @@ import unittest import os -import solcx + import eth_account import eth_typing import eth_utils from eth_account.account import LocalAccount -from web3 import Web3 from solana.rpc.api import Client as SolanaClient from ..plugin.solana_rest_api_tools import get_token_balance_gwei, ether2program -from .testing_helpers import compile_and_deploy_contract +from .testing_helpers import SolidityContractDeployer class TestAirdroppingEthAccounts(unittest.TestCase): @@ -21,8 +20,9 @@ def setUpClass(cls) -> None: new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') - proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) + cls._contract_deployer = SolidityContractDeployer() + cls._web3 = cls._contract_deployer.web3 + solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") cls._solana_client = SolanaClient(solana_url) @@ -34,13 +34,13 @@ def test_airdrop_on_get_balance(self): def test_airdrop_on_deploy(self): contract_owner: LocalAccount = self._web3.eth.account.create() - contract = compile_and_deploy_contract(contract_owner, self._CONTRACT_STORAGE_SOURCE) + contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_STORAGE_SOURCE) actual_balance_wei = self._get_balance_wei(contract.address) self.assertEqual(self._EXPECTED_BALANCE_WEI, actual_balance_wei) def test_airdrop_onto_wrapped_new_address(self): contract_owner: LocalAccount = self._web3.eth.account.create() - contract = compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) + contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._WRAPPER_CONTRACT_STORAGE_SOURCE) nested_contract_address = contract.functions.getNested().call() nested_actual_balance = self._get_balance_wei(nested_contract_address) wrapper_actual_balance = self._get_balance_wei(contract.address) @@ -49,9 +49,8 @@ def test_airdrop_onto_wrapped_new_address(self): def test_airdrop_on_deploy_estimation(self): owner_eth_account: LocalAccount = self._web3.eth.account.create() - compile_result = solcx.compile_source(self._CONTRACT_STORAGE_SOURCE) - _, contract_interface = compile_result.popitem() - contract_data = contract_interface.get("bin") + compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_STORAGE_SOURCE) + contract_data = compiled_info.contract_interface.get("bin") self.assertIsNotNone(contract_data) self._web3.eth.estimate_gas({"from": owner_eth_account.address, "data": contract_data}) owner_balance = self._get_balance_wei(owner_eth_account.address) diff --git a/proxy/testing/test_contract_features.py b/proxy/testing/test_contract_features.py deleted file mode 100644 index e88f1d5f2..000000000 --- a/proxy/testing/test_contract_features.py +++ /dev/null @@ -1,38 +0,0 @@ -import unittest -import os - -import eth_utils - -from web3 import Web3, exceptions as web3_exceptions -from solana.rpc.api import Client as SolanaClient - -from .testing_helpers import compile_contract - - -class TestAirdroppingEthAccounts(unittest.TestCase): - - @classmethod - def setUpClass(cls) -> None: - new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) - cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') - - proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - cls._web3 = Web3(Web3.HTTPProvider(proxy_url)) - solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") - cls._solana_client = SolanaClient(solana_url) - - def test_contract_raises_correct_error(self): - arr_constructable = compile_contract(self._CONTRACT_LIST_CONSTRUCTABLE) - with self.assertRaises(web3_exceptions.ContractLogicError) as cm: - arr_constructable.constructor([]).buildTransaction() - self.assertEqual("ListConstructable: empty list", str(cm.exception)) - - _CONTRACT_LIST_CONSTRUCTABLE = ''' - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.7.0 <0.9.0; - contract ArrConstructable { - constructor(uint256[] memory vector_) payable { - require(vector_.length > 0, "ListConstructable: empty list"); - } - } - ''' diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py new file mode 100644 index 000000000..68cd8dd65 --- /dev/null +++ b/proxy/testing/test_contract_reverting.py @@ -0,0 +1,80 @@ +import unittest +import os + +import eth_utils +from web3 import exceptions as web3_exceptions +from solana.rpc.api import Client as SolanaClient +from eth_account.account import LocalAccount + +from .testing_helpers import SolidityContractDeployer + + +class TestAirdroppingEthAccounts(unittest.TestCase): + + @classmethod + def setUpClass(cls) -> None: + cls._contract_deployer = SolidityContractDeployer() + cls._web3 = cls._contract_deployer.web3 + + new_user_airdrop_amount = int(os.environ.get("NEW_USER_AIRDROP_AMOUNT", "0")) + cls._EXPECTED_BALANCE_WEI = eth_utils.to_wei(new_user_airdrop_amount, 'ether') + + solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") + cls._solana_client = SolanaClient(solana_url) + + def test_constructor_raises_string_based_error(self): + compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + compiled_info.contract.constructor([]).buildTransaction() + self.assertEqual("ListConstructable: empty list", str(cm.exception)) + + _CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT = ''' + pragma solidity >=0.7.0 <0.9.0; + contract ArrConstructable { + constructor(uint256[] memory vector_) payable { + require(vector_.length > 0, "ListConstructable: empty list"); + } + } + ''' + + def test_constructor_raises_no_argument_error(self): + compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_REVERT) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + compiled_info.contract.constructor([]).buildTransaction() + self.assertEqual("execution reverted", str(cm.exception)) + + _CONTRACT_CONSTRUCTOR_REVERT = ''' + pragma solidity >=0.7.0 <0.9.0; + contract ArrConstructable { + constructor(uint256[] memory vector_) payable { + require(vector_.length > 0); + } + } + ''' + + def test_method_raises_string_based_error(self): + contract_owner: LocalAccount = self._web3.eth.account.create() + contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + contract.functions.do_string_based_revert().call() + self.assertEqual("Predefined revert happened", str(cm.exception)) + + def test_method_raises_trivial_error(self): + contract_owner: LocalAccount = self._web3.eth.account.create() + contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) + with self.assertRaises(web3_exceptions.ContractLogicError) as cm: + contract.functions.do_trivial_revert().call() + self.assertEqual("execution reverted", str(cm.exception)) + + _CONTRACT_METHOD_STRING_BASED_REVERT = ''' + pragma solidity >=0.7.0 <0.9.0; + contract ArrConstructable { + function do_string_based_revert() public view returns (uint256) { + require(false, "Predefined revert happened"); + } + function do_trivial_revert() public view returns (uint256) { + require(false); + } + } + ''' + diff --git a/proxy/testing/testing_helpers.py b/proxy/testing/testing_helpers.py index f2a366121..120acd50e 100644 --- a/proxy/testing/testing_helpers.py +++ b/proxy/testing/testing_helpers.py @@ -1,32 +1,48 @@ +import dataclasses import os import solcx from eth_account.account import LocalAccount from web3 import Web3, eth as web3_eth import eth_utils +from typing import Union, Type, Any, Tuple -def compile_contract(solidity_source_code: str): - proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - web3 = Web3(Web3.HTTPProvider(proxy_url)) +@dataclasses.dataclass +class ContractCompiledInfo: + contract_id: Any + contract_interface: Any + contract: web3_eth.Contract - compile_result = solcx.compile_source(solidity_source_code) - contract_id, contract_interface = compile_result.popitem() - contract = web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) - return contract +class SolidityContractDeployer: -def compile_and_deploy_contract(contract_owner: LocalAccount, solidity_source_code: str) -> web3_eth.Contract: - proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') - web3 = Web3(Web3.HTTPProvider(proxy_url)) + _CONTRACT_TYPE = Union[Type[web3_eth.Contract], web3_eth.Contract] - contract = compile_contract(solidity_source_code) - nonce = web3.eth.get_transaction_count(contract_owner.address) - chain_id = web3.eth.chain_id - minimal_gas_price = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei - trx_signed = web3.eth.account.sign_transaction( - dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=minimal_gas_price, to='', value=0, data=contract.bytecode), - contract_owner.key) - trx_hash = web3.eth.send_raw_transaction(trx_signed.rawTransaction) - trx_receipt = web3.eth.wait_for_transaction_receipt(trx_hash) - contract = web3.eth.contract(address=trx_receipt.contractAddress, abi=contract.abi) - return contract + def __init__(self): + proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana') + self._web3 = Web3(Web3.HTTPProvider(proxy_url)) + + def compile_contract(self, solidity_source_code: str) -> ContractCompiledInfo: + """Returns tuple of """ + compile_result = solcx.compile_source(solidity_source_code) + contract_id, contract_interface = compile_result.popitem() + contract = self._web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) + return ContractCompiledInfo(contract_id, contract_interface, contract) + + def compile_and_deploy_contract(self, contract_owner: LocalAccount, solidity_source_code: str) -> _CONTRACT_TYPE: + compiled_info = self.compile_contract(solidity_source_code) + contract = compiled_info.contract + nonce = self._web3.eth.get_transaction_count(contract_owner.address) + chain_id = self._web3.eth.chain_id + minimal_gas_price = int(os.environ.get("MINIMAL_GAS_PRICE", 1)) * eth_utils.denoms.gwei + trx_signed = self._web3.eth.account.sign_transaction( + dict(nonce=nonce, chainId=chain_id, gas=987654321, gasPrice=minimal_gas_price, to='', value=0, data=contract.bytecode), + contract_owner.key) + trx_hash = self._web3.eth.send_raw_transaction(trx_signed.rawTransaction) + trx_receipt = self._web3.eth.wait_for_transaction_receipt(trx_hash) + contract = self._web3.eth.contract(address=trx_receipt.contractAddress, abi=contract.abi) + return contract + + @property + def web3(self) -> Web3: + return self._web3 From b5720a6aaff91aa34d51b4e356407e09518d07e7 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 8 Dec 2021 14:26:14 +0500 Subject: [PATCH 71/95] rename tests airdrop on ci --- ...test_airdropping_eth_accounts.py => test_airdropping_on_ci.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proxy/testing/{test_airdropping_eth_accounts.py => test_airdropping_on_ci.py} (100%) diff --git a/proxy/testing/test_airdropping_eth_accounts.py b/proxy/testing/test_airdropping_on_ci.py similarity index 100% rename from proxy/testing/test_airdropping_eth_accounts.py rename to proxy/testing/test_airdropping_on_ci.py From 685c2a9fa8b06ee0e5f8838229b01d43b218834e Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 8 Dec 2021 14:45:32 +0500 Subject: [PATCH 72/95] rename airdrop on ci tests --- proxy/testing/testing_helpers.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proxy/testing/testing_helpers.py b/proxy/testing/testing_helpers.py index 120acd50e..e9c57c257 100644 --- a/proxy/testing/testing_helpers.py +++ b/proxy/testing/testing_helpers.py @@ -4,13 +4,12 @@ from eth_account.account import LocalAccount from web3 import Web3, eth as web3_eth import eth_utils -from typing import Union, Type, Any, Tuple +from typing import Union, Type, Any, Dict @dataclasses.dataclass class ContractCompiledInfo: - contract_id: Any - contract_interface: Any + contract_interface: Dict contract: web3_eth.Contract @@ -25,9 +24,9 @@ def __init__(self): def compile_contract(self, solidity_source_code: str) -> ContractCompiledInfo: """Returns tuple of """ compile_result = solcx.compile_source(solidity_source_code) - contract_id, contract_interface = compile_result.popitem() + _, contract_interface = compile_result.popitem() contract = self._web3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) - return ContractCompiledInfo(contract_id, contract_interface, contract) + return ContractCompiledInfo(contract_interface, contract) def compile_and_deploy_contract(self, contract_owner: LocalAccount, solidity_source_code: str) -> _CONTRACT_TYPE: compiled_info = self.compile_contract(solidity_source_code) From 4531d2d59d6773a8d2aae05c5b1d7119361fabea Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 8 Dec 2021 17:19:11 +0500 Subject: [PATCH 73/95] Spit and polish --- proxy/common_neon/emulator_interactor.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 72e05e0a4..fddd104cc 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -4,30 +4,34 @@ from .errors import EthereumError from ..environment import neon_cli - logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) def call_emulated(contract_id, caller_id, data=None, value=None): output = emulator(contract_id, caller_id, data, value) - logger.debug("call_emulated %s %s %s %s return %s", contract_id, caller_id, data, value, output) + logger.debug(f"Call emulated. contract_id: {contract_id}, caller_id: {caller_id}, data: {data}, value: {value}, return: {output}") result = json.loads(output) exit_status = result['exit_status'] if exit_status == 'revert': result_value = result['result'] - if len(result_value) < 8 or result_value[:8] != '08c379a0': - raise EthereumError(code=3, message='execution reverted') + raise_eth_err_by_revert(result_value) - offset = int(result_value[8:8+64], 16) - length = int(result_value[8+64:8+64+64], 16) - message = str(bytes.fromhex(result_value[8+offset*2+64:8+offset*2+64+length*2]), 'utf8') - raise EthereumError(code=3, message=message, data='0x'+result_value) if result["exit_status"] != "succeed": raise Exception("evm emulator error ", result) return result +def raise_eth_err_by_revert(result_value: str): + if len(result_value) < 8 or result_value[:8] != '08c379a0': + raise EthereumError(code=3, message='execution reverted') + + offset = int(result_value[8:8 + 64], 16) + length = int(result_value[8 + 64:8 + 64 + 64], 16) + message = str(bytes.fromhex(result_value[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') + raise EthereumError(code=3, message=message, data='0x' + result_value) + + def emulator(contract, sender, data, value): data = data or "none" value = value or "" From 6e2a308038616ab227f26f6af6910c70a8b2e544 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 9 Dec 2021 13:54:11 +0500 Subject: [PATCH 74/95] Check check --- proxy/common_neon/emulator_interactor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index fddd104cc..56982d3a8 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -24,12 +24,12 @@ def call_emulated(contract_id, caller_id, data=None, value=None): def raise_eth_err_by_revert(result_value: str): if len(result_value) < 8 or result_value[:8] != '08c379a0': - raise EthereumError(code=3, message='execution reverted') + raise EthereumError(code=3, message='Transaction reverted') offset = int(result_value[8:8 + 64], 16) length = int(result_value[8 + 64:8 + 64 + 64], 16) message = str(bytes.fromhex(result_value[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') - raise EthereumError(code=3, message=message, data='0x' + result_value) + raise EthereumError(code=3, message="Transaction reverted with " + message + ".", data='0x' + result_value) def emulator(contract, sender, data, value): From 3cbbee0762959d005ed547ee3cbe59b463a92523 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 9 Dec 2021 14:59:03 +0500 Subject: [PATCH 75/95] Check check --- proxy/testing/test_contract_reverting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index 68cd8dd65..8349e0c36 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -26,7 +26,7 @@ def test_constructor_raises_string_based_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: compiled_info.contract.constructor([]).buildTransaction() - self.assertEqual("ListConstructable: empty list", str(cm.exception)) + self.assertEqual("Transaction reverted with ListConstructable: empty list.", str(cm.exception)) _CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; @@ -41,7 +41,7 @@ def test_constructor_raises_no_argument_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: compiled_info.contract.constructor([]).buildTransaction() - self.assertEqual("execution reverted", str(cm.exception)) + self.assertEqual("Transaction reverted", str(cm.exception)) _CONTRACT_CONSTRUCTOR_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; @@ -57,14 +57,14 @@ def test_method_raises_string_based_error(self): contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: contract.functions.do_string_based_revert().call() - self.assertEqual("Predefined revert happened", str(cm.exception)) + self.assertEqual("Transaction reverted with Predefined revert happened.", str(cm.exception)) def test_method_raises_trivial_error(self): contract_owner: LocalAccount = self._web3.eth.account.create() contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: contract.functions.do_trivial_revert().call() - self.assertEqual("execution reverted", str(cm.exception)) + self.assertEqual("Transaction reverted", str(cm.exception)) _CONTRACT_METHOD_STRING_BASED_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; From b76824024412f84c880c32a8bca9b97fff88880b Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 15 Dec 2021 22:31:09 +0500 Subject: [PATCH 76/95] Spit and polish --- ..._airdropping_on_ci.py => test_airdropping_with_no_faucet.py} | 0 proxy/testing/test_contract_reverting.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename proxy/testing/{test_airdropping_on_ci.py => test_airdropping_with_no_faucet.py} (100%) diff --git a/proxy/testing/test_airdropping_on_ci.py b/proxy/testing/test_airdropping_with_no_faucet.py similarity index 100% rename from proxy/testing/test_airdropping_on_ci.py rename to proxy/testing/test_airdropping_with_no_faucet.py diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index 8349e0c36..34e03db68 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -9,7 +9,7 @@ from .testing_helpers import SolidityContractDeployer -class TestAirdroppingEthAccounts(unittest.TestCase): +class TestContractReverting(unittest.TestCase): @classmethod def setUpClass(cls) -> None: From 3a07c1b2730ccda316d9a41c3fd11a6355ed5256 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 15 Dec 2021 22:33:48 +0500 Subject: [PATCH 77/95] Rollback some --- proxy/common_neon/emulator_interactor.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 56982d3a8..42ffc18b5 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -24,13 +24,12 @@ def call_emulated(contract_id, caller_id, data=None, value=None): def raise_eth_err_by_revert(result_value: str): if len(result_value) < 8 or result_value[:8] != '08c379a0': - raise EthereumError(code=3, message='Transaction reverted') - + raise EthereumError(code=3, message='execution reverted') + offset = int(result_value[8:8 + 64], 16) length = int(result_value[8 + 64:8 + 64 + 64], 16) message = str(bytes.fromhex(result_value[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') - raise EthereumError(code=3, message="Transaction reverted with " + message + ".", data='0x' + result_value) - + raise EthereumError(code=3, message='execution reverted: ' + message, data='0x' + result_value) def emulator(contract, sender, data, value): data = data or "none" From 99ce2b7349e9f610759c31e7dfd5a4bb2bd69779 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Thu, 16 Dec 2021 16:37:27 +0500 Subject: [PATCH 78/95] Fix tests --- proxy/testing/test_contract_reverting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index 34e03db68..f7aff4ff8 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -26,7 +26,7 @@ def test_constructor_raises_string_based_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: compiled_info.contract.constructor([]).buildTransaction() - self.assertEqual("Transaction reverted with ListConstructable: empty list.", str(cm.exception)) + self.assertEqual("execution reverted: ListConstructable: empty list", str(cm.exception)) _CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; @@ -41,7 +41,7 @@ def test_constructor_raises_no_argument_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: compiled_info.contract.constructor([]).buildTransaction() - self.assertEqual("Transaction reverted", str(cm.exception)) + self.assertEqual("execution reverted", str(cm.exception)) _CONTRACT_CONSTRUCTOR_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; @@ -57,14 +57,14 @@ def test_method_raises_string_based_error(self): contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: contract.functions.do_string_based_revert().call() - self.assertEqual("Transaction reverted with Predefined revert happened.", str(cm.exception)) + self.assertEqual("execution reverted: Predefined revert happened", str(cm.exception)) def test_method_raises_trivial_error(self): contract_owner: LocalAccount = self._web3.eth.account.create() contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: contract.functions.do_trivial_revert().call() - self.assertEqual("Transaction reverted", str(cm.exception)) + self.assertEqual("execution reverted", str(cm.exception)) _CONTRACT_METHOD_STRING_BASED_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; From d9b9dcbe5759c0f4a6e88054d66e8db99b886434 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 20 Dec 2021 21:43:59 +0500 Subject: [PATCH 79/95] Spit and polish --- proxy/common_neon/emulator_interactor.py | 28 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 42ffc18b5..efc82fda1 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -1,6 +1,7 @@ import json import logging +from typing import Optional, Dict, Any from .errors import EthereumError from ..environment import neon_cli @@ -12,25 +13,38 @@ def call_emulated(contract_id, caller_id, data=None, value=None): output = emulator(contract_id, caller_id, data, value) logger.debug(f"Call emulated. contract_id: {contract_id}, caller_id: {caller_id}, data: {data}, value: {value}, return: {output}") result = json.loads(output) + check_emulated_exit_status(result) + return result + + +def check_emulated_exit_status(result: Dict[str, Any]): exit_status = result['exit_status'] if exit_status == 'revert': - result_value = result['result'] - raise_eth_err_by_revert(result_value) + result_value = decode_revert_message(result['result']) + message = 'execution reverted: ' + result_value if result_value is not None else 'execution reverted' + raise EthereumError(code=3, message=message, data='0x' + result_value) if result["exit_status"] != "succeed": raise Exception("evm emulator error ", result) - return result + + +def decode_revert_message(data) -> Optional[str]: + if len(data) < 8 or data[:8] != '08c379a0': + return None + offset = int(data[8:8 + 64], 16) + length = int(data[8 + 64:8 + 64 + 64], 16) + message = str(bytes.fromhex(data[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') + return message def raise_eth_err_by_revert(result_value: str): if len(result_value) < 8 or result_value[:8] != '08c379a0': raise EthereumError(code=3, message='execution reverted') - - offset = int(result_value[8:8 + 64], 16) - length = int(result_value[8 + 64:8 + 64 + 64], 16) - message = str(bytes.fromhex(result_value[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') + + raise EthereumError(code=3, message='execution reverted: ' + message, data='0x' + result_value) + def emulator(contract, sender, data, value): data = data or "none" value = value or "" From 29b361abad0003df6c16cc2078c9179a28fceae9 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 20 Dec 2021 21:48:17 +0500 Subject: [PATCH 80/95] Spit and polish --- proxy/common_neon/emulator_interactor.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index efc82fda1..305af1826 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -37,14 +37,6 @@ def decode_revert_message(data) -> Optional[str]: return message -def raise_eth_err_by_revert(result_value: str): - if len(result_value) < 8 or result_value[:8] != '08c379a0': - raise EthereumError(code=3, message='execution reverted') - - - raise EthereumError(code=3, message='execution reverted: ' + message, data='0x' + result_value) - - def emulator(contract, sender, data, value): data = data or "none" value = value or "" From 967d79f5acf8b5be97231add45a6168c6a1cdbf0 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 20 Dec 2021 22:03:55 +0500 Subject: [PATCH 81/95] new test --- proxy/testing/test_contract_reverting.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index f7aff4ff8..bc3b4c1b3 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -7,6 +7,7 @@ from eth_account.account import LocalAccount from .testing_helpers import SolidityContractDeployer +from ..common_neon.emulator_interactor import decode_revert_message class TestContractReverting(unittest.TestCase): @@ -22,6 +23,15 @@ def setUpClass(cls) -> None: solana_url = os.environ.get("SOLANA_URL", "http://localhost:8899") cls._solana_client = SolanaClient(solana_url) + def test_revert_message_decoding(self): + revert_message = decode_revert_message(self._STRING_BASED_REVERT_DATA) + self.assertEqual(revert_message, "execution reverted: Not enough Ether provided.") + + _STRING_BASED_REVERT_DATA = "0x08c379a0" \ + "0000000000000000000000000000000000000000000000000000000000000020" \ + "000000000000000000000000000000000000000000000000000000000000001a" \ + "4e6f7420656e6f7567682045746865722070726f76696465642e000000000000" + def test_constructor_raises_string_based_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: From 174ec4737100df752dfeacfac876f0774ffff82e Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 20 Dec 2021 22:17:07 +0500 Subject: [PATCH 82/95] Fix tests --- proxy/testing/test_contract_reverting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index bc3b4c1b3..abd285a3d 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -25,9 +25,9 @@ def setUpClass(cls) -> None: def test_revert_message_decoding(self): revert_message = decode_revert_message(self._STRING_BASED_REVERT_DATA) - self.assertEqual(revert_message, "execution reverted: Not enough Ether provided.") + self.assertEqual(revert_message, "Not enough Ether provided.") - _STRING_BASED_REVERT_DATA = "0x08c379a0" \ + _STRING_BASED_REVERT_DATA = "08c379a0" \ "0000000000000000000000000000000000000000000000000000000000000020" \ "000000000000000000000000000000000000000000000000000000000000001a" \ "4e6f7420656e6f7567682045746865722070726f76696465642e000000000000" From 98c6aae332e37c36bc4c4e87358e0b68d5ca7bec Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Mon, 20 Dec 2021 23:38:29 +0500 Subject: [PATCH 83/95] Spit and polish --- proxy/common_neon/emulator_interactor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 305af1826..44d9bbcb1 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -21,8 +21,10 @@ def check_emulated_exit_status(result: Dict[str, Any]): exit_status = result['exit_status'] if exit_status == 'revert': result_value = decode_revert_message(result['result']) - message = 'execution reverted: ' + result_value if result_value is not None else 'execution reverted' - raise EthereumError(code=3, message=message, data='0x' + result_value) + if result_value is None: + raise EthereumError(code=3, message='execution reverted') + else: + raise EthereumError(code=3, message='execution reverted: ' + result_value, data='0x' + result_value) if result["exit_status"] != "succeed": raise Exception("evm emulator error ", result) From 1aca196d61c0386ea6318f94cd43cbaa4f3cea66 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 21 Dec 2021 17:11:29 +0500 Subject: [PATCH 84/95] Add an extra checks on decode revert message --- proxy/common_neon/emulator_interactor.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 44d9bbcb1..5a2438d3d 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -31,10 +31,28 @@ def check_emulated_exit_status(result: Dict[str, Any]): def decode_revert_message(data) -> Optional[str]: - if len(data) < 8 or data[:8] != '08c379a0': + if len(data) == 0: + logger.debug(f"Empty reverting signature: {len(data)}, data: 0x{data.hex()}") return None + + if len(data) < 8: + raise Exception(f"To less bytes to decode reverting signature: {len(data)}, data: 0x{data.hex()}") + + if data[:8] != '08c379a0': + logger.debug(f"Failed to decode revert_message, unknown revert signature: {data[:8]}") + return None + + if len(data) < 8 + 64: + raise Exception(f"Too less bytes to decode revert msg offset: {len(data)}, data: 0x{data.hex()}") offset = int(data[8:8 + 64], 16) - length = int(data[8 + 64:8 + 64 + 64], 16) + + if len(data) < 8 + offset * 2 + 64: + raise Exception(f"Too less bytes to decode revert msg len: {len(data)}, data: 0x{data.hex()}") + length = int(data[8 + offset * 2:8 + offset * 2 + 64], 16) + + if len(data) < 8 + offset * 2 + 64 + length * 2: + raise Exception(f"Too less bytes to decode revert msg: {len(data)}, data: 0x{data.hex()}") + message = str(bytes.fromhex(data[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') return message From 97645c3ded04171c4fbf7a838634b0bf4941af77 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 21 Dec 2021 17:27:13 +0500 Subject: [PATCH 85/95] Get rid of hex on str typed revert data --- proxy/common_neon/emulator_interactor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 5a2438d3d..c3c297efa 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -32,26 +32,26 @@ def check_emulated_exit_status(result: Dict[str, Any]): def decode_revert_message(data) -> Optional[str]: if len(data) == 0: - logger.debug(f"Empty reverting signature: {len(data)}, data: 0x{data.hex()}") + logger.debug(f"Empty reverting signature: {len(data)}, data: 0x{data}") return None if len(data) < 8: - raise Exception(f"To less bytes to decode reverting signature: {len(data)}, data: 0x{data.hex()}") + raise Exception(f"To less bytes to decode reverting signature: {len(data)}, data: 0x{data}") if data[:8] != '08c379a0': logger.debug(f"Failed to decode revert_message, unknown revert signature: {data[:8]}") return None if len(data) < 8 + 64: - raise Exception(f"Too less bytes to decode revert msg offset: {len(data)}, data: 0x{data.hex()}") + raise Exception(f"Too less bytes to decode revert msg offset: {len(data)}, data: 0x{data}") offset = int(data[8:8 + 64], 16) if len(data) < 8 + offset * 2 + 64: - raise Exception(f"Too less bytes to decode revert msg len: {len(data)}, data: 0x{data.hex()}") + raise Exception(f"Too less bytes to decode revert msg len: {len(data)}, data: 0x{data}") length = int(data[8 + offset * 2:8 + offset * 2 + 64], 16) if len(data) < 8 + offset * 2 + 64 + length * 2: - raise Exception(f"Too less bytes to decode revert msg: {len(data)}, data: 0x{data.hex()}") + raise Exception(f"Too less bytes to decode revert msg: {len(data)}, data: 0x{data}") message = str(bytes.fromhex(data[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') return message From c9e34dad13e4f9a685769f39c43f56a96133eab4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 21 Dec 2021 18:52:58 +0500 Subject: [PATCH 86/95] Process empty revert data other way --- proxy/common_neon/emulator_interactor.py | 12 ++++++++---- proxy/plugin/solana_rest_api.py | 1 + proxy/testing/test_contract_reverting.py | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index c3c297efa..c86c9a4b1 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -20,19 +20,23 @@ def call_emulated(contract_id, caller_id, data=None, value=None): def check_emulated_exit_status(result: Dict[str, Any]): exit_status = result['exit_status'] if exit_status == 'revert': - result_value = decode_revert_message(result['result']) + revert_data = result['result'] + logger.debug(f"Got revert call emulated result with data: {revert_data}") + result_value = decode_revert_message(revert_data) if result_value is None: - raise EthereumError(code=3, message='execution reverted') + raise EthereumError(code=3, message='') else: raise EthereumError(code=3, message='execution reverted: ' + result_value, data='0x' + result_value) - if result["exit_status"] != "succeed": + exit_status = result["exit_status"] + if exit_status != "succeed": + logger.debug(f"Got emulate exit_status: {exit_status}") raise Exception("evm emulator error ", result) def decode_revert_message(data) -> Optional[str]: if len(data) == 0: - logger.debug(f"Empty reverting signature: {len(data)}, data: 0x{data}") + logger.debug(f"Empty revert data") return None if len(data) < 8: diff --git a/proxy/plugin/solana_rest_api.py b/proxy/plugin/solana_rest_api.py index a09e3585b..1c42e2bb6 100644 --- a/proxy/plugin/solana_rest_api.py +++ b/proxy/plugin/solana_rest_api.py @@ -49,6 +49,7 @@ NEON_PROXY_PKG_VERSION = '0.5.1-dev' NEON_PROXY_REVISION = 'NEON_PROXY_REVISION_TO_BE_REPLACED' + class EthereumModel: def __init__(self): self.signer = self.get_solana_account() diff --git a/proxy/testing/test_contract_reverting.py b/proxy/testing/test_contract_reverting.py index abd285a3d..cf54e73b9 100644 --- a/proxy/testing/test_contract_reverting.py +++ b/proxy/testing/test_contract_reverting.py @@ -51,7 +51,7 @@ def test_constructor_raises_no_argument_error(self): compiled_info = self._contract_deployer.compile_contract(self._CONTRACT_CONSTRUCTOR_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: compiled_info.contract.constructor([]).buildTransaction() - self.assertEqual("execution reverted", str(cm.exception)) + self.assertEqual("", str(cm.exception)) _CONTRACT_CONSTRUCTOR_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; @@ -74,7 +74,7 @@ def test_method_raises_trivial_error(self): contract = self._contract_deployer.compile_and_deploy_contract(contract_owner, self._CONTRACT_METHOD_STRING_BASED_REVERT) with self.assertRaises(web3_exceptions.ContractLogicError) as cm: contract.functions.do_trivial_revert().call() - self.assertEqual("execution reverted", str(cm.exception)) + self.assertEqual("", str(cm.exception)) _CONTRACT_METHOD_STRING_BASED_REVERT = ''' pragma solidity >=0.7.0 <0.9.0; From 7e318160b1f29cd5fec856ffd9ba39ad22dcf31f Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 21 Dec 2021 20:46:26 +0500 Subject: [PATCH 87/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index c86c9a4b1..22876612c 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -40,7 +40,7 @@ def decode_revert_message(data) -> Optional[str]: return None if len(data) < 8: - raise Exception(f"To less bytes to decode reverting signature: {len(data)}, data: 0x{data}") + raise Exception(f"To less bytes to decode revert signature: {len(data)}, data: 0x{data}") if data[:8] != '08c379a0': logger.debug(f"Failed to decode revert_message, unknown revert signature: {data[:8]}") From 4e9472c036aa73068cc72ff7ce9b8992e54de10e Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Tue, 21 Dec 2021 23:07:06 +0500 Subject: [PATCH 88/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 22876612c..a38d88730 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -28,7 +28,6 @@ def check_emulated_exit_status(result: Dict[str, Any]): else: raise EthereumError(code=3, message='execution reverted: ' + result_value, data='0x' + result_value) - exit_status = result["exit_status"] if exit_status != "succeed": logger.debug(f"Got emulate exit_status: {exit_status}") raise Exception("evm emulator error ", result) From c4bd1c34932dfb640f229ecf2744bd8ff331b112 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 10:25:24 +0500 Subject: [PATCH 89/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index a38d88730..f49aff1de 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -29,32 +29,32 @@ def check_emulated_exit_status(result: Dict[str, Any]): raise EthereumError(code=3, message='execution reverted: ' + result_value, data='0x' + result_value) if exit_status != "succeed": - logger.debug(f"Got emulate exit_status: {exit_status}") + logger.debug(f"Got not succeed emulate exit_status: {exit_status}") raise Exception("evm emulator error ", result) def decode_revert_message(data) -> Optional[str]: - if len(data) == 0: - logger.debug(f"Empty revert data") + data_len = len(data) + if data_len == 0: return None - if len(data) < 8: - raise Exception(f"To less bytes to decode revert signature: {len(data)}, data: 0x{data}") + if data_len < 8: + raise Exception(f"Too less bytes to decode revert signature: {data_len}, data: 0x{data}") if data[:8] != '08c379a0': logger.debug(f"Failed to decode revert_message, unknown revert signature: {data[:8]}") return None - if len(data) < 8 + 64: - raise Exception(f"Too less bytes to decode revert msg offset: {len(data)}, data: 0x{data}") + if data_len < 8 + 64: + raise Exception(f"Too less bytes to decode revert msg offset: {data_len}, data: 0x{data}") offset = int(data[8:8 + 64], 16) - if len(data) < 8 + offset * 2 + 64: - raise Exception(f"Too less bytes to decode revert msg len: {len(data)}, data: 0x{data}") + if data_len < 8 + offset * 2 + 64: + raise Exception(f"Too less bytes to decode revert msg len: {data_len}, data: 0x{data}") length = int(data[8 + offset * 2:8 + offset * 2 + 64], 16) - if len(data) < 8 + offset * 2 + 64 + length * 2: - raise Exception(f"Too less bytes to decode revert msg: {len(data)}, data: 0x{data}") + if data_len < 8 + offset * 2 + 64 + length * 2: + raise Exception(f"Too less bytes to decode revert msg: {data_len}, data: 0x{data}") message = str(bytes.fromhex(data[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') return message From 631e122caf69b2bbf780e7c0302f009e1390849c Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 12:31:44 +0500 Subject: [PATCH 90/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index f49aff1de..d87af7be8 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -47,16 +47,16 @@ def decode_revert_message(data) -> Optional[str]: if data_len < 8 + 64: raise Exception(f"Too less bytes to decode revert msg offset: {data_len}, data: 0x{data}") - offset = int(data[8:8 + 64], 16) + offset = int(data[8:8 + 64], 16) * 2 if data_len < 8 + offset * 2 + 64: raise Exception(f"Too less bytes to decode revert msg len: {data_len}, data: 0x{data}") - length = int(data[8 + offset * 2:8 + offset * 2 + 64], 16) + length = int(data[8 + offset:8 + offset + 64], 16) - if data_len < 8 + offset * 2 + 64 + length * 2: + if data_len < 8 + offset + 64 + length * 2: raise Exception(f"Too less bytes to decode revert msg: {data_len}, data: 0x{data}") - message = str(bytes.fromhex(data[8 + offset * 2 + 64:8 + offset * 2 + 64 + length * 2]), 'utf8') + message = str(bytes.fromhex(data[8 + offset + 64:8 + offset + 64 + length * 2]), 'utf8') return message From 00104ba9c727a91a16cfcf4c70983a4bd421e308 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 12:32:17 +0500 Subject: [PATCH 91/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index d87af7be8..56555c549 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -51,12 +51,12 @@ def decode_revert_message(data) -> Optional[str]: if data_len < 8 + offset * 2 + 64: raise Exception(f"Too less bytes to decode revert msg len: {data_len}, data: 0x{data}") - length = int(data[8 + offset:8 + offset + 64], 16) + length = int(data[8 + offset:8 + offset + 64], 16) * 2 - if data_len < 8 + offset + 64 + length * 2: + if data_len < 8 + offset + 64 + length: raise Exception(f"Too less bytes to decode revert msg: {data_len}, data: 0x{data}") - message = str(bytes.fromhex(data[8 + offset + 64:8 + offset + 64 + length * 2]), 'utf8') + message = str(bytes.fromhex(data[8 + offset + 64:8 + offset + 64 + length]), 'utf8') return message From b6ef371abb6d6e42e6876a865ef31b9e03a5e491 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 12:32:37 +0500 Subject: [PATCH 92/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index 56555c549..e0924e437 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -49,7 +49,7 @@ def decode_revert_message(data) -> Optional[str]: raise Exception(f"Too less bytes to decode revert msg offset: {data_len}, data: 0x{data}") offset = int(data[8:8 + 64], 16) * 2 - if data_len < 8 + offset * 2 + 64: + if data_len < 8 + offset + 64: raise Exception(f"Too less bytes to decode revert msg len: {data_len}, data: 0x{data}") length = int(data[8 + offset:8 + offset + 64], 16) * 2 From 05a4af2563cc241dd23234b3f1738e05336481d4 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 13:25:45 +0500 Subject: [PATCH 93/95] spit and polish --- proxy/common_neon/emulator_interactor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/common_neon/emulator_interactor.py b/proxy/common_neon/emulator_interactor.py index e0924e437..b8d4720d6 100644 --- a/proxy/common_neon/emulator_interactor.py +++ b/proxy/common_neon/emulator_interactor.py @@ -33,7 +33,7 @@ def check_emulated_exit_status(result: Dict[str, Any]): raise Exception("evm emulator error ", result) -def decode_revert_message(data) -> Optional[str]: +def decode_revert_message(data: str) -> Optional[str]: data_len = len(data) if data_len == 0: return None From 5309fc9c21e0fd084fb81d1d9f3c6efaf802b4d7 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 13:29:39 +0500 Subject: [PATCH 94/95] spit and polish --- proxy/testing/test_airdropping_with_no_faucet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/testing/test_airdropping_with_no_faucet.py b/proxy/testing/test_airdropping_with_no_faucet.py index a74117819..2aab45171 100644 --- a/proxy/testing/test_airdropping_with_no_faucet.py +++ b/proxy/testing/test_airdropping_with_no_faucet.py @@ -63,7 +63,6 @@ def _get_balance_wei(self, eth_account: str) -> int: self.assertIsInstance(balance, int) return balance * eth_utils.denoms.gwei - _CONTRACT_STORAGE_SOURCE = ''' // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; From 7d9f01b0a7ee863b3c724b86de2578eb1036ad29 Mon Sep 17 00:00:00 2001 From: rozhkovdmitrii Date: Wed, 22 Dec 2021 13:31:04 +0500 Subject: [PATCH 95/95] spit and polish --- proxy/testing/test_airdropping_with_no_faucet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/testing/test_airdropping_with_no_faucet.py b/proxy/testing/test_airdropping_with_no_faucet.py index 2aab45171..036ec909f 100644 --- a/proxy/testing/test_airdropping_with_no_faucet.py +++ b/proxy/testing/test_airdropping_with_no_faucet.py @@ -1,7 +1,6 @@ import unittest import os - import eth_account import eth_typing import eth_utils