Skip to content

Commit 42bd209

Browse files
author
rozhkovdmitrii
committed
Merge branch 'develop' into 517-suppress-neon-cli-stderr
2 parents fc6d8ac + e1f883e commit 42bd209

File tree

9 files changed

+236
-21
lines changed

9 files changed

+236
-21
lines changed

proxy/common_neon/layouts.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
from construct import Bytes, Int8ul, Int64ul
2+
from construct import Bytes, Int8ul, Int32ul, Int64ul
33
from construct import Struct
44

55
STORAGE_ACCOUNT_INFO_LAYOUT = Struct(
@@ -30,6 +30,12 @@
3030
"ro_blocked_cnt" / Int8ul,
3131
)
3232

33+
CODE_ACCOUNT_INFO_LAYOUT = Struct(
34+
"type" / Int8ul,
35+
"owner" / Bytes(32),
36+
"code_size" / Int32ul,
37+
)
38+
3339

3440
CREATE_ACCOUNT_LAYOUT = Struct(
3541
"lamports" / Int64ul,

proxy/indexer/accounts_db.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from .utils import BaseDB, str_fmt_object
2+
3+
4+
class NeonAccountInfo:
5+
def __init__(self, neon_account: str = None, pda_account: str = None, code_account: str = None, slot: int = 0, code: str = None):
6+
self.neon_account = neon_account
7+
self.pda_account = pda_account
8+
self.code_account = code_account
9+
self.slot = slot
10+
self.code = code
11+
12+
def __str__(self):
13+
return str_fmt_object(self)
14+
15+
16+
class NeonAccountDB(BaseDB):
17+
def __init__(self):
18+
BaseDB.__init__(self)
19+
20+
def _create_table_sql(self) -> str:
21+
self._table_name = 'neon_accounts'
22+
return f"""
23+
CREATE TABLE IF NOT EXISTS {self._table_name} (
24+
neon_account CHAR(42),
25+
pda_account VARCHAR(50),
26+
code_account VARCHAR(50),
27+
slot BIGINT,
28+
code TEXT,
29+
30+
UNIQUE(pda_account, code_account)
31+
);"""
32+
33+
def set_acc_by_request(self, neon_account: str, pda_account: str, code_account: str, code: str):
34+
with self._conn.cursor() as cursor:
35+
cursor.execute(f'''
36+
INSERT INTO {self._table_name}(neon_account, pda_account, code_account, slot, code)
37+
VALUES(%s, %s, %s, %s, %s)
38+
ON CONFLICT (pda_account, code_account) DO UPDATE
39+
SET
40+
code=EXCLUDED.code
41+
;
42+
''',
43+
(neon_account, pda_account, code_account, 0, code))
44+
45+
def set_acc_indexer(self, neon_account: str, pda_account: str, code_account: str, slot: int):
46+
with self._conn.cursor() as cursor:
47+
cursor.execute(f'''
48+
INSERT INTO {self._table_name}(neon_account, pda_account, code_account, slot)
49+
VALUES(%s, %s, %s, %s)
50+
ON CONFLICT (pda_account, code_account) DO UPDATE
51+
SET
52+
slot=EXCLUDED.slot
53+
;
54+
''',
55+
(neon_account, pda_account, code_account, slot))
56+
57+
def _acc_from_value(self, value) -> NeonAccountInfo:
58+
self.debug(f"accounts db returned {value}")
59+
60+
if not value:
61+
return NeonAccountInfo()
62+
63+
return NeonAccountInfo(
64+
neon_account=value[0],
65+
pda_account=value[1],
66+
code_account=value[2],
67+
slot=value[3],
68+
code=value[4]
69+
)
70+
71+
def get_account_info(self, account) -> NeonAccountInfo:
72+
return self._acc_from_value(
73+
self._fetchone(['neon_account', 'pda_account', 'code_account', 'slot', 'code'],
74+
[('neon_account', account)],
75+
['slot desc']))

proxy/indexer/indexer.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
import base58
44
import time
55
from logged_groups import logged_group
6+
from solana.system_program import SYS_PROGRAM_ID
67

78
try:
89
from indexer_base import IndexerBase
910
from indexer_db import IndexerDB
1011
from utils import SolanaIxSignInfo, NeonTxResultInfo, NeonTxInfo, Canceller, str_fmt_object
11-
from utils import get_accounts_from_storage
12+
from utils import get_accounts_from_storage, check_error
1213
except ImportError:
1314
from .indexer_base import IndexerBase
1415
from .indexer_db import IndexerDB
1516
from .utils import SolanaIxSignInfo, NeonTxResultInfo, NeonTxInfo, Canceller, str_fmt_object
16-
from .utils import get_accounts_from_storage
17+
from .utils import get_accounts_from_storage, check_error
1718

1819
from ..environment import EVM_LOADER_ID, FINALIZED, CANCEL_TIMEOUT, SOLANA_URL
1920

@@ -252,6 +253,9 @@ def iter_txs(self):
252253
for tx in self._tx_table.values():
253254
yield tx
254255

256+
def add_account_to_db(self, neon_account: str, pda_account: str, code_account: str, slot: int):
257+
self._db.fill_account_info_by_indexer(neon_account, pda_account, code_account, slot)
258+
255259

256260
@logged_group("neon.Indexer")
257261
class DummyIxDecoder:
@@ -451,6 +455,50 @@ def _decode_datachunck(self, ix_info: SolanaIxInfo) -> WriteIxDecoder._DataChunk
451455
)
452456

453457

458+
class CreateAccountIxDecoder(DummyIxDecoder):
459+
def __init__(self, state: ReceiptsParserState):
460+
DummyIxDecoder.__init__(self, 'CreateAccount', state)
461+
462+
def execute(self) -> bool:
463+
self._decoding_start()
464+
465+
if check_error(self.ix.tx):
466+
return self._decoding_skip("Ignore failed create account")
467+
468+
if len(self.ix.ix_data) < 41:
469+
return self._decoding_skip(f'not enough data to get the Neon account {len(self.ix.ix_data)}')
470+
471+
neon_account = "0x" + self.ix.ix_data[8+8+4:][:20].hex()
472+
pda_account = self.ix.get_account(1)
473+
code_account = self.ix.get_account(3)
474+
if code_account == str(SYS_PROGRAM_ID) or code_account == '':
475+
code_account = None
476+
477+
self.debug(f"neon_account({neon_account}), pda_account({pda_account}), code_account({code_account}), slot({self.ix.sign.slot})")
478+
479+
self.state.add_account_to_db(neon_account, pda_account, code_account, self.ix.sign.slot)
480+
return True
481+
482+
483+
class ResizeStorageAccountIxDecoder(DummyIxDecoder):
484+
def __init__(self, state: ReceiptsParserState):
485+
DummyIxDecoder.__init__(self, 'ResizeStorageAccount', state)
486+
487+
def execute(self) -> bool:
488+
self._decoding_start()
489+
490+
if check_error(self.ix.tx):
491+
return self._decoding_skip("Ignore failed resize account")
492+
493+
pda_account = self.ix.get_account(0)
494+
code_account = self.ix.get_account(2)
495+
496+
self.debug(f"pda_account({pda_account}), code_account({code_account}), slot({self.ix.sign.slot})")
497+
498+
self.state.add_account_to_db(None, pda_account, code_account, self.ix.sign.slot)
499+
return True
500+
501+
454502
class CallFromRawIxDecoder(DummyIxDecoder):
455503
def __init__(self, state: ReceiptsParserState):
456504
DummyIxDecoder.__init__(self, 'CallFromRaw', state)
@@ -635,7 +683,7 @@ def __init__(self, solana_url, evm_loader_id):
635683
self.ix_decoder_map = {
636684
0x00: WriteIxDecoder(self.state),
637685
0x01: DummyIxDecoder('Finalize', self.state),
638-
0x02: DummyIxDecoder('CreateAccount', self.state),
686+
0x02: CreateAccountIxDecoder(self.state),
639687
0x03: DummyIxDecoder('Call', self.state),
640688
0x04: DummyIxDecoder('CreateAccountWithSeed', self.state),
641689
0x05: CallFromRawIxDecoder(self.state),
@@ -647,6 +695,7 @@ def __init__(self, solana_url, evm_loader_id):
647695
0x0c: CancelIxDecoder(self.state),
648696
0x0d: PartialCallOrContinueIxDecoder(self.state),
649697
0x0e: ExecuteOrContinueIxParser(self.state),
698+
0x11: ResizeStorageAccountIxDecoder(self.state),
650699
0x12: WriteWithHolderIxDecoder(self.state),
651700
0x13: PartialCallV02IxDecoder(self.state),
652701
0x14: ContinueV02IxDecoder(self.state),

proxy/indexer/indexer_db.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66

77
try:
88
from utils import LogDB, NeonTxInfo, NeonTxResultInfo, SolanaIxSignInfo
9+
from accounts_db import NeonAccountDB, NeonAccountInfo
910
from blocks_db import SolanaBlocksDB, SolanaBlockDBInfo
1011
from transactions_db import NeonTxsDB, NeonTxDBInfo
1112
from pending_db import NeonPendingTxInfo, NeonPendingTxsDB, PendingTxError
1213
from sql_dict import SQLDict
14+
from utils import get_code_from_account, get_accounts_by_neon_address
1315
except ImportError:
1416
from .utils import LogDB, NeonTxInfo, NeonTxResultInfo, SolanaIxSignInfo
17+
from .accounts_db import NeonAccountDB, NeonAccountInfo
1518
from .blocks_db import SolanaBlocksDB, SolanaBlockDBInfo
1619
from .transactions_db import NeonTxsDB, NeonTxDBInfo
1720
from .pending_db import NeonPendingTxInfo, NeonPendingTxsDB, PendingTxError
1821
from .sql_dict import SQLDict
22+
from .utils import get_code_from_account, get_accounts_by_neon_address
1923

2024

2125
@logged_group("neon.Indexer")
@@ -25,6 +29,7 @@ def __init__(self):
2529
self._blocks_db = SolanaBlocksDB()
2630
self._txs_db = NeonTxsDB()
2731
self._pending_txs_db = NeonPendingTxsDB()
32+
self._account_db = NeonAccountDB()
2833
self._client = None
2934

3035
self._constants = SQLDict(tablename="constants")
@@ -52,7 +57,6 @@ def submit_transaction(self, neon_tx: NeonTxInfo, neon_res: NeonTxResultInfo, us
5257
rec['blockNumber'] = hex(block.height)
5358
self._logs_db.push_logs(neon_res.logs, block)
5459
tx = NeonTxDBInfo(neon_tx=neon_tx, neon_res=neon_res, block=block, used_ixs=used_ixs)
55-
self.debug(f'submit_transaction NeonTxDBInfo {tx}')
5660
self._txs_db.set_tx(tx)
5761
except Exception as err:
5862
err_tb = "".join(traceback.format_tb(err.__traceback__))
@@ -76,6 +80,27 @@ def _fill_block_from_net(self, block: SolanaBlockDBInfo):
7680
self._blocks_db.set_block(block)
7781
return block
7882

83+
def _fill_account_data_from_net(self, account: NeonAccountInfo):
84+
got_changes = False
85+
if not account.pda_account:
86+
pda_account, code_account = get_accounts_by_neon_address(self._client, account.neon_account)
87+
if pda_account:
88+
account.pda_account = pda_account
89+
account.code_account = code_account
90+
got_changes = True
91+
if account.code_account:
92+
code = get_code_from_account(self._client, account.code_account)
93+
if code:
94+
account.code = code
95+
got_changes = True
96+
if got_changes:
97+
self._account_db.set_acc_by_request(
98+
account.neon_account,
99+
account.pda_account,
100+
account.code_account,
101+
account.code)
102+
return account
103+
79104
def get_block_by_slot(self, slot) -> SolanaBlockDBInfo:
80105
block = self._blocks_db.get_block_by_slot(slot)
81106
if not block.hash:
@@ -134,6 +159,19 @@ def get_tx_by_neon_sign(self, neon_sign: str) -> NeonTxDBInfo:
134159
tx.block = self.get_block_by_slot(tx.neon_res.slot)
135160
return tx
136161

162+
def get_contract_code(self, address) -> str:
163+
account = self._account_db.get_account_info(address)
164+
if not account.neon_account or (account.code_account and not account.code):
165+
if not account.neon_account:
166+
account.neon_account = address
167+
account = self._fill_account_data_from_net(account)
168+
if account.code:
169+
return account.code
170+
return '0x'
171+
172+
def fill_account_info_by_indexer(self, neon_account: str, pda_account: str, code_account: str, slot: int):
173+
self._account_db.set_acc_indexer(neon_account, pda_account, code_account, slot)
174+
137175
def del_not_finalized(self, from_slot: int, to_slot: int):
138176
for d in [self._logs_db, self._blocks_db, self._txs_db, self._pending_txs_db]:
139177
d.del_not_finalized(from_slot=from_slot, to_slot=to_slot)

proxy/indexer/utils.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
from spl.token.instructions import get_associated_token_address
2222
from logged_groups import logged_group
2323

24+
from ..common_neon.address import ether2program
2425
from ..common_neon.constants import SYSVAR_INSTRUCTION_PUBKEY, INCINERATOR_PUBKEY, KECCAK_PROGRAM
25-
from ..common_neon.layouts import STORAGE_ACCOUNT_INFO_LAYOUT
26+
from ..common_neon.layouts import STORAGE_ACCOUNT_INFO_LAYOUT, CODE_ACCOUNT_INFO_LAYOUT, ACCOUNT_INFO_LAYOUT
2627
from ..common_neon.eth_proto import Trx as EthTx
28+
from ..common_neon.utils import get_from_dict
2729
from ..environment import SOLANA_URL, EVM_LOADER_ID, ETH_TOKEN_MINT_ID, get_solana_accounts
2830

2931
from proxy.indexer.pg_common import POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST
@@ -35,8 +37,6 @@
3537

3638
def check_error(trx):
3739
if 'meta' in trx and 'err' in trx['meta'] and trx['meta']['err'] is not None:
38-
# logger.debug("Got err trx")
39-
# logger.debug("\n{}".format(json.dumps(trx['meta']['err'])))
4040
return True
4141
return False
4242

@@ -262,6 +262,42 @@ def get_accounts_from_storage(client, storage_account, *, logger):
262262
return None
263263

264264

265+
@logged_group("neon.Indexer")
266+
def get_accounts_by_neon_address(client: Client, neon_address, *, logger):
267+
pda_address, _nonce = ether2program(neon_address)
268+
reciept = client.get_account_info(pda_address, commitment=Confirmed)
269+
account_info = get_from_dict(reciept, 'result', 'value')
270+
if account_info is None:
271+
logger.debug(f"account_info is None for pda_address({pda_address}) in reciept({reciept})")
272+
return None, None
273+
data = base64.b64decode(account_info['data'][0])
274+
if len(data) < ACCOUNT_INFO_LAYOUT.sizeof():
275+
logger.debug(f"{len(data)} < {ACCOUNT_INFO_LAYOUT.sizeof()}")
276+
return None, None
277+
account = ACCOUNT_INFO_LAYOUT.parse(data)
278+
code_account = None
279+
if account.code_account != [0]*32:
280+
code_account = str(PublicKey(account.code_account))
281+
return pda_address, code_account
282+
283+
284+
@logged_group("neon.Indexer")
285+
def get_code_from_account(client: Client, address, *, logger):
286+
reciept = client.get_account_info(address, commitment=Confirmed)
287+
code_account_info = get_from_dict(reciept, 'result', 'value')
288+
if code_account_info is None:
289+
logger.debug(f"code_account_info is None for code_address({address}) in reciept({reciept})")
290+
return None
291+
data = base64.b64decode(code_account_info['data'][0])
292+
if len(data) < CODE_ACCOUNT_INFO_LAYOUT.sizeof():
293+
return None
294+
storage = CODE_ACCOUNT_INFO_LAYOUT.parse(data)
295+
offset = CODE_ACCOUNT_INFO_LAYOUT.sizeof()
296+
if len(data) < offset + storage.code_size:
297+
return None
298+
return '0x' + data[offset:][:storage.code_size].hex()
299+
300+
265301
@logged_group("neon.Indexer")
266302
class BaseDB:
267303
def __init__(self):

proxy/plugin/solana_rest_api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
modelInstanceLock = threading.Lock()
4444
modelInstance = None
4545

46-
NEON_PROXY_PKG_VERSION = '0.5.4-dev'
46+
NEON_PROXY_PKG_VERSION = '0.6.0-dev'
4747
NEON_PROXY_REVISION = 'NEON_PROXY_REVISION_TO_BE_REPLACED'
4848

4949

@@ -337,8 +337,9 @@ def eth_getTransactionByHash(self, trxId):
337337
return None
338338
return self._getTransaction(tx)
339339

340-
def eth_getCode(self, param, param1):
341-
return "0x01"
340+
def eth_getCode(self, account, _tag):
341+
account = account.lower()
342+
return self.db.get_contract_code(account)
342343

343344
def eth_sendTransaction(self, trx):
344345
self.debug("eth_sendTransaction")

0 commit comments

Comments
 (0)