Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Last changes from devnet tests #578

Merged
merged 34 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
af391f9
Request full block info only on demand.
afalaleev Feb 10, 2022
1e5421c
Set steps to 250
afalaleev Feb 10, 2022
334fc18
Refactor custom storage errors x1 and x4
afalaleev Feb 10, 2022
d8618fe
Set steps to 350
afalaleev Feb 10, 2022
d3dbc64
Return back 500 steps
afalaleev Feb 11, 2022
4b62623
Repeat retry on bad storage status
afalaleev Feb 11, 2022
6803dfd
Return back bad storage cases.
afalaleev Feb 11, 2022
8046d71
Fix trx counter parser
afalaleev Feb 11, 2022
48024f0
Add counter to name of trasaction sender
afalaleev Feb 11, 2022
a989412
Fix types in solana-interactor
afalaleev Feb 11, 2022
3843139
Fix building errors
afalaleev Feb 11, 2022
ec7c039
Add environment variable SKIP_PREFLIGHT
afalaleev Feb 11, 2022
df28db0
Shift time for memdb
afalaleev Feb 11, 2022
8e2d9a9
Fix loading transaction from db.
afalaleev Feb 11, 2022
0b854a9
Fix eth_getBlockByNumber
afalaleev Feb 11, 2022
7882c48
Each worker has its own block mem db
afalaleev Feb 11, 2022
e7f8580
Fast latest block
afalaleev Feb 11, 2022
d1e5733
Minimize number of requests to Solana for get block
afalaleev Feb 11, 2022
a668b87
Only one request of slot list per worker
afalaleev Feb 11, 2022
283ccd0
Use cache of DB for block by number.
afalaleev Feb 11, 2022
78452ee
Fix empty logs
afalaleev Feb 11, 2022
7a0596d
Add funcName to pretty_log_cfg.json
afalaleev Feb 13, 2022
7fa46f8
Fix typpo
afalaleev Feb 13, 2022
0066ab9
Cache of blocks without indexer
afalaleev Feb 13, 2022
bb09bb0
Add implementation for universal transactions
afalaleev Feb 13, 2022
8ef460b
Show storage error in result
afalaleev Feb 13, 2022
71cc9d9
Simplify logs
afalaleev Feb 13, 2022
c2ca5ce
Add generate of fake history for not-finalized transactions
afalaleev Feb 14, 2022
eb52c37
Move PendingTxError to common_neon.errors
afalaleev Feb 14, 2022
f1e2afd
Remove testing classes from proxy
afalaleev Feb 14, 2022
c5aa13d
Add cache of blocks from solana
afalaleev Feb 15, 2022
9806d05
Disable test
afalaleev Feb 15, 2022
5b9f802
Cache of blocks per worker
afalaleev Feb 16, 2022
0fb68a5
Add first initialization of block list in memdb
afalaleev Feb 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pretty_log_cfg.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"formatters": {
"standard": {
"format": "%(asctime)s.%(msecs)03d [%(levelname)-.1s] P:%(process)d %(class)s:%(lineno)d %(context)s %(message)s",
"format": "%(asctime)s.%(msecs)03d [%(levelname)-.1s] P:%(process)d %(name)s:%(class)s:%(funcName)s:%(lineno)d %(context)s %(message)s",
"style": "%",
"datefmt": "%Y-%m-%d %H:%M:%S"
},
Expand Down
4 changes: 0 additions & 4 deletions proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from .proxy import entry_point
from .proxy import main, start
from .proxy import Proxy
from .testing.test_case import TestCase

__all__ = [
# PyPi package entry_point. See
Expand All @@ -20,8 +19,5 @@
# Embed proxy.py. See
# https://github.com/abhinavsingh/proxy.py#embed-proxypy
'main', 'start',
# Unit testing with proxy.py. See
# https://github.com/abhinavsingh/proxy.py#unit-testing-with-proxypy
'TestCase',
'Proxy',
]
2 changes: 1 addition & 1 deletion proxy/common_neon/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ class AccountInfo(NamedTuple):
@staticmethod
def frombytes(data):
cont = ACCOUNT_INFO_LAYOUT.parse(data)
return AccountInfo(cont.ether, cont.trx_count, PublicKey(cont.code_account))
return AccountInfo(cont.ether, int.from_bytes(cont.trx_count, 'little'), PublicKey(cont.code_account))
4 changes: 4 additions & 0 deletions proxy/common_neon/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ class SolanaAccountNotFoundError(Exception):
"""Provides special error processing"""
def __init__(self):
super().__init__(SolanaErrors.AccountNotFound.value)


class PendingTxError(Exception):
pass
58 changes: 47 additions & 11 deletions proxy/common_neon/eth_proto.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from ecdsa import SigningKey, SECP256k1, VerifyingKey
from sha3 import keccak_256
import json
import rlp
from eth_keys import keys

Expand All @@ -21,6 +19,9 @@
'''


class InvalidTrx(Exception):
pass


class Trx(rlp.Serializable):
fields = (
Expand All @@ -35,6 +36,9 @@ class Trx(rlp.Serializable):
('s', rlp.codec.big_endian_int)
)

secpk1n = 115792089237316195423570985008687907852837564279074904382605163141518161494337
null_address = b'\xff' * 20

def __init__(self, *args, **kwargs):
rlp.Serializable.__init__(self, *args, **kwargs)
self._msg = None
Expand All @@ -44,29 +48,60 @@ def fromString(cls, s):
return rlp.decode(s, Trx)

def chainId(self):
# chainid*2 + 35 xxxxx0 + 100011 xxxx0 + 100010 +1
# chainid*2 + 36 xxxxx0 + 100100 xxxx0 + 100011 +1
return (self.v-1)//2 - 17
if self.v in (27, 28):
return None
elif self.v >= 37:
# chainid*2 + 35 xxxxx0 + 100011 xxxx0 + 100010 +1
# chainid*2 + 36 xxxxx0 + 100100 xxxx0 + 100011 +1
return ((self.v - 1) // 2) - 17
else:
raise InvalidTrx("Invalid V value")

def _unsigned_msg(self):
chain_id = self.chainId()
if chain_id is None:
return rlp.encode((self.nonce, self.gasPrice, self.gasLimit, self.toAddress, self.value, self.callData))
else:
return rlp.encode((self.nonce, self.gasPrice, self.gasLimit, self.toAddress, self.value, self.callData,
chain_id, 0, 0), Trx)

def unsigned_msg(self):
if self._msg is None:
self._msg = rlp.encode((self.nonce, self.gasPrice, self.gasLimit, self.toAddress, self.value, self.callData, self.chainId(), b"", b""))
self._msg = self._unsigned_msg()
return self._msg

def _signature(self):
return keys.Signature(vrs=[1 if self.v % 2 == 0 else 0, self.r, self.s])

def signature(self):
return keys.Signature(vrs=[1 if self.v % 2 == 0 else 0, self.r, self.s]).to_bytes()
return self._signature().to_bytes()

def _sender(self):
hash = keccak_256(self.unsigned_msg()).digest()
sig = keys.Signature(vrs=[1 if self.v % 2 == 0 else 0, self.r, self.s])
pub = sig.recover_public_key_from_msg_hash(hash)
if self.r == 0 and self.s == 0:
return self.null_address
elif self.v in (27, 28):
pass
elif self.v >= 37:
vee = self.v - self.chainId() * 2 - 8
assert vee in (27, 28)
else:
raise InvalidTrx("Invalid V value")

if self.r >= self.secpk1n or self.s >= self.secpk1n or self.r == 0 or self.s == 0:
raise InvalidTrx("Invalid signature values!")

sighash = keccak_256(self._unsigned_msg()).digest()
sig = self._signature()
pub = sig.recover_public_key_from_msg_hash(sighash)

return pub.to_canonical_address()

def sender(self):
return self._sender().hex()

def hash_signed(self):
return keccak_256(rlp.encode((self.nonce, self.gasPrice, self.gasLimit, self.toAddress, self.value, self.callData,
return keccak_256(rlp.encode((self.nonce, self.gasPrice, self.gasLimit,
self.toAddress, self.value, self.callData,
self.v, self.r, self.s))).digest()

def contract(self):
Expand All @@ -75,6 +110,7 @@ def contract(self):
contract_addr = rlp.encode((self._sender(), self.nonce))
return keccak_256(contract_addr).digest()[-20:].hex()


#class JsonEncoder(json.JSONEncoder):
# def default(self, obj):
# if isinstance(obj, bytes):
Expand Down
32 changes: 18 additions & 14 deletions proxy/common_neon/solana_interactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import re
import time

from typing import Optional

from solana.blockhash import Blockhash
from solana.publickey import PublicKey
from solana.rpc.api import Client as SolanaClient
Expand All @@ -16,7 +18,7 @@

from .costs import update_transaction_cost
from .utils import get_from_dict, SolanaBlockInfo
from ..environment import EVM_LOADER_ID, CONFIRMATION_CHECK_DELAY, WRITE_TRANSACTION_COST_IN_DB
from ..environment import EVM_LOADER_ID, CONFIRMATION_CHECK_DELAY, WRITE_TRANSACTION_COST_IN_DB, SKIP_PREFLIGHT
from ..environment import LOG_SENDING_SOLANA_TRANSACTION, FUZZING_BLOCKHASH, CONFIRM_TIMEOUT, FINALIZED

from ..common_neon.layouts import ACCOUNT_INFO_LAYOUT
Expand Down Expand Up @@ -77,7 +79,7 @@ def _send_rpc_batch_request(self, method: str, params_list: List[Any]) -> List[R

return full_response_data

def get_account_info(self, storage_account) -> AccountInfo:
def get_account_info(self, storage_account) -> Optional[AccountInfo]:
opts = {
"encoding": "base64",
"commitment": "confirmed",
Expand Down Expand Up @@ -133,7 +135,7 @@ def get_multiple_accounts_info(self, accounts: [PublicKey]) -> [AccountInfo]:
def get_sol_balance(self, account):
return self.client.get_balance(account, commitment=Confirmed)['result']['value']

def get_neon_account_info(self, eth_account: EthereumAddress) -> NeonAccountInfo:
def get_neon_account_info(self, eth_account: EthereumAddress) -> Optional[NeonAccountInfo]:
account_sol, nonce = ether2program(eth_account)
info = self.get_account_info(account_sol)
if info is None:
Expand Down Expand Up @@ -179,7 +181,7 @@ def get_block_info_list(self, block_slot_list: [int], commitment='confirmed') ->
net_block = response['result']
block = SolanaBlockInfo(
slot=slot,
finalized=(commitment==FINALIZED),
finalized=(commitment == FINALIZED),
hash='0x' + base58.b58decode(net_block['blockhash']).hex(),
height=net_block['blockHeight'],
parent_hash='0x' + base58.b58decode(net_block['previousBlockhash']).hex(),
Expand All @@ -189,14 +191,14 @@ def get_block_info_list(self, block_slot_list: [int], commitment='confirmed') ->
block_list.append(block)
return block_list

def get_recent_blockslot(self) -> int:
blockhash_resp = self.client.get_recent_blockhash(commitment=Confirmed)
def get_recent_blockslot(self, commitment=Confirmed) -> int:
blockhash_resp = self.client.get_recent_blockhash(commitment=commitment)
if not blockhash_resp["result"]:
raise RuntimeError("failed to get recent blockhash")
return blockhash_resp['result']['context']['slot']

def get_recent_blockhash(self) -> Blockhash:
blockhash_resp = self.client.get_recent_blockhash(commitment=Confirmed)
def get_recent_blockhash(self, commitment=Confirmed) -> Blockhash:
blockhash_resp = self.client.get_recent_blockhash(commitment=commitment)
if not blockhash_resp["result"]:
raise RuntimeError("failed to get recent blockhash")
blockhash = blockhash_resp["result"]["value"]["blockhash"]
Expand Down Expand Up @@ -239,7 +241,7 @@ def _fuzzing_transactions(self, signer: SolanaAccount, tx_list, tx_opts, request

def _send_multiple_transactions_unconfirmed(self, signer: SolanaAccount, tx_list: [Transaction]) -> [str]:
opts = {
"skipPreflight": False,
"skipPreflight": SKIP_PREFLIGHT,
"encoding": "base64",
"preflightCommitment": "confirmed"
}
Expand Down Expand Up @@ -290,7 +292,7 @@ def get_measurements(self, reason, eth_tx, receipt):
return

try:
self.debug(f"send multiple transactions for reason {reason}: {eth_tx.__dict__}")
self.debug(f"send multiple transactions for reason {reason}")

measurements = self._extract_measurements_from_receipt(receipt)
for m in measurements:
Expand Down Expand Up @@ -454,9 +456,10 @@ def check_if_storage_is_empty_error(receipt):
if error_arr is not None and isinstance(error_arr, list):
error_dict = error_arr[1]
if isinstance(error_dict, dict) and 'Custom' in error_dict:
if error_dict['Custom'] == 1 or error_dict['Custom'] == 4:
return True
return False
custom = error_dict['Custom']
if custom in (1, 4):
return custom
return 0


def get_logs_from_receipt(receipt):
Expand All @@ -480,7 +483,7 @@ def get_logs_from_receipt(receipt):
if log_from_prepared_receipt is not None:
return log_from_prepared_receipt

return None
return []


@logged_group("neon.Proxy")
Expand All @@ -489,6 +492,7 @@ def check_if_accounts_blocked(receipt, *, logger):
if logs is None:
logger.error("Can't get logs")
logger.info("Failed result: %s"%json.dumps(receipt, indent=3))
return False

ro_blocked = "trying to execute transaction on ro locked account"
rw_blocked = "trying to execute transaction on rw locked account"
Expand Down
Loading