Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
20 changes: 0 additions & 20 deletions proxy/common_neon/solana_interactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,26 +341,6 @@ def check_if_storage_is_empty_error(receipt):
return False


def check_if_continue_returned(result):
tx_info = result
accounts = tx_info["transaction"]["message"]["accountKeys"]
evm_loader_instructions = []

for idx, instruction in enumerate(tx_info["transaction"]["message"]["instructions"]):
if accounts[instruction["programIdIndex"]] == EVM_LOADER_ID:
evm_loader_instructions.append(idx)

for inner in (tx_info['meta']['innerInstructions']):
if inner["index"] in evm_loader_instructions:
for event in inner['instructions']:
if accounts[event['programIdIndex']] == EVM_LOADER_ID:
instruction = base58.b58decode(event['data'])[:1]
if int().from_bytes(instruction, "little") == 6: # OnReturn evmInstruction code
return tx_info['transaction']['signatures'][0]

return None


def get_logs_from_reciept(receipt):
log_from_reciept = get_from_dict(receipt, 'result', 'meta', 'logMessages')
if log_from_reciept is not None:
Expand Down
33 changes: 18 additions & 15 deletions proxy/common_neon/transaction_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

from base58 import b58encode
from sha3 import keccak_256
from solana.publickey import PublicKey
from solana.rpc.api import SendTransactionError
from solana.sysvar import *
from solana.transaction import AccountMeta, Transaction

Expand All @@ -22,11 +20,11 @@
from .emulator_interactor import call_emulated
from .layouts import ACCOUNT_INFO_LAYOUT
from .neon_instruction import NeonInstruction
from .solana_interactor import SolanaInteractor, check_if_continue_returned, check_for_errors,\
from .solana_interactor import SolanaInteractor, check_for_errors,\
check_if_program_exceeded_instructions, check_if_accounts_blocked, get_logs_from_reciept
from ..environment import EVM_LOADER_ID, RETRY_ON_BLOCKED
from ..plugin.eth_proto import Trx as EthTrx

from ..indexer.utils import NeonTxResultInfo
from ..common_neon.eth_proto import Trx as EthTrx

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -335,7 +333,7 @@ def call_signed_noniterative(self):
else:
raise Exception(json.dumps(result['meta']))
else:
return result['transaction']['signatures'][0]
return (NeonTxResultInfo(result), result['transaction']['signatures'][0])


class IterativeTransactionSender:
Expand All @@ -344,8 +342,9 @@ class IterativeTransactionSender:
CONTINUE_HOLDER_COMB = 'ExecuteTrxFromAccountDataIterativeOrContinue'

class ContinueReturn:
def __init__(self, success_signature, none_receipts, logs, found_errors, try_one_step, retry_on_blocked, step_count):
def __init__(self, success_signature, neon_res, none_receipts, logs, found_errors, try_one_step, retry_on_blocked, step_count):
self.success_signature = success_signature
self.neon_res = neon_res
self.none_receipts = none_receipts
self.logs = logs
self.found_errors = found_errors
Expand Down Expand Up @@ -432,7 +431,7 @@ def call_continue(self):
continue_result = self.send_and_confirm_continue(trxs, none_receipts)

if continue_result.success_signature is not None:
return continue_result.success_signature
return (continue_result.neon_res, continue_result.success_signature)
none_receipts = continue_result.none_receipts
logs += continue_result.logs
found_errors = continue_result.found_errors or found_errors
Expand All @@ -448,7 +447,7 @@ def call_continue(self):
continue_result = self.send_and_confirm_continue([trx], none_receipts, retry_on_blocked, step_count)

if continue_result.success_signature is not None:
return continue_result.success_signature
return (continue_result.neon_res, continue_result.success_signature)
none_receipts = continue_result.none_receipts
logs += continue_result.logs
found_errors = continue_result.found_errors or found_errors
Expand Down Expand Up @@ -477,14 +476,17 @@ def call_cancel(self):

logger.debug("Cancel")
result = self.sender.send_measured_transaction(trx, self.eth_trx, 'CancelWithNonce')
return result['transaction']['signatures'][0]
neon_res = NeonTxResultInfo()
neon_res.slot = result['slot']
return (neon_res, result['transaction']['signatures'][0])


def send_and_confirm_continue(self, trxs: List[Transaction], none_receipts: List[str], retry_on_blocked: int = 1, step_count: int = 1) -> ContinueReturn:
found_errors = False
try_one_step = False
logs = []
success_signature = None
success_neon_res = None

receipts = self.sender.send_multiple_transactions_unconfirmed(trxs)
receipts += none_receipts
Expand All @@ -497,9 +499,10 @@ def send_and_confirm_continue(self, trxs: List[Transaction], none_receipts: List
if not check_error(result):
self.success_steps += 1
self.sender.get_measurements(result)
signature = check_if_continue_returned(result)
if signature is not None:
success_signature = signature
neon_res = NeonTxResultInfo(result)
if neon_res.is_valid():
success_signature = result['transaction']['signatures'][0]
success_neon_res = neon_res
elif check_if_accounts_blocked(result):
logger.debug("Blocked account")
retry_on_blocked -= 1
Expand All @@ -514,8 +517,7 @@ def send_and_confirm_continue(self, trxs: List[Transaction], none_receipts: List
found_errors = True
else:
none_receipts.append(receipt)

return self.ContinueReturn(success_signature, none_receipts, logs, found_errors, try_one_step, retry_on_blocked, step_count)
return self.ContinueReturn(success_signature, success_neon_res, none_receipts, logs, found_errors, try_one_step, retry_on_blocked, step_count)


def steps_count(self):
Expand Down Expand Up @@ -551,3 +553,4 @@ def make_combined_trx(self, steps, index):
return self.instruction.make_partial_call_or_continue_from_account_data(steps, index)
else:
raise Exception("Unknown continue type: {}".format(self.instruction_type))

111 changes: 111 additions & 0 deletions proxy/indexer/blocks_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from .utils import BaseDB, str_fmt_object


class SolanaBlockDBInfo:
def __init__(self, slot=None, finalized=False, height=None, hash=None, parent_hash=None, time=None, signs=None):
self.slot = slot
self.finalized = finalized
self.height = height
self.hash = hash
self.parent_hash = parent_hash
self.time = time
self.signs = signs

def __str__(self):
return str_fmt_object(self)


class SolanaBlocksDB(BaseDB):
def __init__(self):
BaseDB.__init__(self)
self._column_lst = ('slot', 'finalized', 'height', 'hash')
self._full_column_lst = ('slot', 'finalized', 'height', 'hash', 'parent_hash', 'blocktime', 'signatures')

def _create_table_sql(self) -> str:
self._table_name = 'solana_blocks'
return f"""
CREATE TABLE IF NOT EXISTS {self._table_name} (
slot BIGINT,
finalized BOOLEAN,
height BIGINT,
hash CHAR(66),

parent_hash CHAR(66),
blocktime BIGINT,
signatures BYTEA,

UNIQUE(slot)
);
CREATE INDEX IF NOT EXISTS {self._table_name}_hash ON {self._table_name}(hash);
CREATE INDEX IF NOT EXISTS {self._table_name}_height ON {self._table_name}(height);
"""

def _block_from_value(self, value, slot=None) -> SolanaBlockDBInfo:
if not value:
return SolanaBlockDBInfo(slot=slot)

return SolanaBlockDBInfo(
slot=value[0],
finalized=value[1],
height=value[2],
hash=value[3],
)

def _full_block_from_value(self, value, slot=None) -> SolanaBlockDBInfo:
if not value:
return SolanaBlockDBInfo(slot=slot)

return SolanaBlockDBInfo(
slot=value[0],
finalized=value[1],
height=value[2],
hash=value[3],
parent_hash=value[4],
time=value[5],
signs=self.decode_list(value[6])
)

def get_block_by_slot(self, block_slot) -> SolanaBlockDBInfo:
return self._block_from_value(self._fetchone(self._column_lst, [('slot', block_slot)]), block_slot)

def get_full_block_by_slot(self, block_slot) -> SolanaBlockDBInfo:
return self._full_block_from_value(self._fetchone(self._full_column_lst, [('slot', block_slot)]), block_slot)

def get_block_by_hash(self, block_hash) -> SolanaBlockDBInfo:
return self._block_from_value(self._fetchone(self._column_lst, [('hash', block_hash)]))

def get_block_by_height(self, block_num) -> SolanaBlockDBInfo:
return self._block_from_value(self._fetchone(self._column_lst, [('height', block_num)]))

def set_block(self, block: SolanaBlockDBInfo):
cursor = self._conn.cursor()
cursor.execute(f'''
INSERT INTO {self._table_name}
({', '.join(self._full_column_lst)})
VALUES
({', '.join(['%s' for _ in range(len(self._full_column_lst))])})
ON CONFLICT (slot) DO UPDATE SET
hash=EXCLUDED.hash,
height=EXCLUDED.height,
parent_hash=EXCLUDED.parent_hash,
blocktime=EXCLUDED.blocktime,
signatures=EXCLUDED.signatures
''',
(block.slot, block.finalized, block.height, block.hash,
block.parent_hash, block.time, self.encode_list(block.signs)))

def fill_block_height(self, height, slots):
rows = []
for slot in slots:
rows.append((slot, height))
height += 1

cursor = self._conn.cursor()
cursor.executemany(
f'INSERT INTO {self._table_name}(slot, finalized, height) VALUES(%s, True, %s) ON CONFLICT DO NOTHING',
rows)

def del_not_finalized(self, from_slot: int, to_slot: int):
cursor = self._conn.cursor()
cursor.execute(f'DELETE FROM {self._table_name} WHERE slot >= %s AND slot <= %s AND finalized = false',
(from_slot, to_slot))
Loading