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

Create a new service for the Private RPC API #50

Merged
merged 2 commits into from
Jun 23, 2024
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
49 changes: 0 additions & 49 deletions _old/_proxy/neon_rpc_api_model/neon_rpc_api_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,52 +81,3 @@ def eth_sendTransaction(self, tx: Dict[str, Any]) -> str:
tx = self.eth_signTransaction(tx)
return self.eth_sendRawTransaction(tx['raw'])


@staticmethod
def _mp_pool_tx(neon_tx_info: NeonTxInfo) -> Dict[str, Any]:
to_addr = NeonAddress.from_raw(neon_tx_info.to_addr)
if to_addr:
to_addr = to_addr.checksum_address

return {
'blockHash': '0x' + '0' * 64,
'blockNumber': None,
'transactionIndex': None,
'from': NeonAddress.from_raw(neon_tx_info.addr).checksum_address,
'gas': hex(neon_tx_info.gas_limit),
'gasPrice': hex(neon_tx_info.gas_price),
'hash': neon_tx_info.sig,
'input': neon_tx_info.calldata,
'nonce': hex(neon_tx_info.nonce),
'to': to_addr,
'value': hex(neon_tx_info.value),
'chainId': hex(neon_tx_info.chain_id) if neon_tx_info.has_chain_id else None
}

def _mp_pool_queue(self, tx_list: List[NeonTxInfo]) -> Dict[str, Any]:
sender_addr = ''
sender_pool: Dict[int, Any] = dict()
sender_pool_dict: Dict[str, Any] = dict()
for tx in tx_list:
if sender_addr != tx.addr and len(sender_addr):
sender_pool_dict[sender_addr] = sender_pool
sender_pool = dict()

sender_addr = tx.addr
sender_pool[tx.nonce] = self._mp_pool_tx(tx)

if sender_addr:
sender_pool_dict[sender_addr] = sender_pool

return sender_pool_dict

def txpool_content(self) -> Dict[str, Any]:
result_dict: Dict[str, Any] = dict()

req_id = get_req_id_from_log()
content = self._mempool_client.get_content(req_id)

result_dict['pending'] = self._mp_pool_queue(content.pending_list)
result_dict['queued'] = self._mp_pool_queue(content.queued_list)
return result_dict

12 changes: 12 additions & 0 deletions common/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class Config:
# Statistic configuration
gather_stat_name: Final[str] = "GATHER_STATISTICS"
# Proxy configuration
rpc_private_ip_name: Final[str] = "RPC_PRIVATE_IP"
rpc_private_port_name: Final[str] = "RPC_PRIVATE_PORT"
rpc_public_port_name: Final[str] = "RPC_PUBLIC_PORT"
rpc_process_cnt_name: Final[str] = "RPC_PROCESS_COUNT"
rpc_worker_cnt_name: Final[str] = "RPC_WORKER_COUNT"
Expand Down Expand Up @@ -432,6 +434,14 @@ def gather_stat(self) -> bool:
#########################
# Proxy configuration

@cached_property
def rpc_private_ip(self) -> str:
return os.environ.get(self.rpc_private_ip_name, self.base_service_ip)

@cached_property
def rpc_private_port(self) -> int:
return self._env_num(self.rpc_private_port_name, self.rpc_public_port + 1, 8000, 25000)

@cached_property
def rpc_public_port(self) -> int:
return self._env_num(self.rpc_public_port_name, 9090, 8000, 25000)
Expand Down Expand Up @@ -832,6 +842,8 @@ def to_string(self) -> str:
self.gather_stat_name: self.gather_stat,
self.debug_cmd_line_name: self.debug_cmd_line,
# Proxy configuration
self.rpc_private_ip_name: self.rpc_private_ip,
self.rpc_private_port_name: self.rpc_private_port,
self.rpc_public_port_name: self.rpc_public_port,
self.rpc_process_cnt_name: self.rpc_process_cnt,
self.rpc_worker_cnt_name: self.rpc_worker_cnt,
Expand Down
8 changes: 6 additions & 2 deletions common/ethereum/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import eth_utils
from typing_extensions import Self

from ..utils.cached import cached_method
from ..utils.cached import cached_method, cached_property
from ..utils.format import hex_to_bytes, bytes_to_hex
from ..utils.pydantic import PlainValidator, PlainSerializer

Expand Down Expand Up @@ -52,7 +52,7 @@ def from_raw(cls, raw: _RawHash) -> Self:
@classmethod
def from_not_none(cls, raw: _RawHash) -> Self:
if raw is None:
raise ValueError(f"Wrong input: null")
raise ValueError("Wrong input: null")
return cls.from_raw(raw)

@property
Expand Down Expand Up @@ -127,6 +127,10 @@ class EthHash32(_BaseHash):
HashSize: ClassVar[int] = 32
ZeroHash: ClassVar[str] = "0x" + "00" * HashSize

@cached_property
def ident(self) -> str:
return self.to_bytes()[:4].hex()

def to_string(self, default: str | None = None) -> str | None:
return self._to_string() if self._data else default

Expand Down
5 changes: 4 additions & 1 deletion common/http/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ def set_property_value(self, name: str, value) -> Self:
self._prop_name_set.add(name)
return self

def get_property_value(self, name: str, default):
return getattr(self, name, default)


@dataclass(frozen=True)
class HttpMethod:
Expand Down Expand Up @@ -173,7 +176,7 @@ def http_validate_method_name(name: str) -> None:
def _validate_request_id(value: HttpRequestId) -> HttpRequestId:
if (value is None) or isinstance(value, int) or isinstance(value, str):
return value
raise ValueError(f"'id' must be a string or integer")
raise ValueError("'id' must be a string or integer")


HttpRequestIdField = Annotated[HttpRequestId, PlainValidator(_validate_request_id)]
24 changes: 19 additions & 5 deletions common/neon/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import random
from typing import Final, Annotated, Union

import eth_account
import eth_keys
import eth_utils
from pydantic.functional_serializers import PlainSerializer
from pydantic.functional_validators import PlainValidator
from typing_extensions import Self

from ..ethereum.hash import EthAddress
from .transaction_model import NeonTxModel
from ..utils.cached import cached_method, cached_property
from ..utils.format import bytes_to_hex, hex_to_bytes, hex_to_int

Expand Down Expand Up @@ -92,11 +94,14 @@ def from_dict(cls, data: _DictAccount | NeonAccount) -> Self:
return cls(address=address, chain_id=chain_id, private_key=private_key)

@classmethod
def from_private_key(cls, pk_data: str | bytes, chain_id: int) -> Self:
pk_data = hex_to_bytes(pk_data)
if len(pk_data) < 32:
raise ValueError(f"Not enough data for private key: {len(pk_data)}")
return cls.from_raw(eth_keys.keys.PrivateKey(pk_data[:32]), chain_id)
def from_private_key(cls, pk_data: str | bytes | eth_keys.keys.PrivateKey, chain_id: int) -> Self:
if isinstance(pk_data, str):
pk_data = hex_to_bytes(pk_data)
if isinstance(pk_data, bytes):
if len(pk_data) < 32:
raise ValueError(f"Not enough data for private key: {len(pk_data)}")
pk_data = eth_keys.keys.PrivateKey(pk_data[:32])
return cls.from_raw(pk_data, chain_id)

def to_dict(self: NeonAccount) -> _DictAccount:
res = dict(
Expand Down Expand Up @@ -145,6 +150,15 @@ def private_key(self) -> eth_keys.keys.PrivateKey:
assert self._private_key
return self._private_key

def sign_msg(self, data: bytes) -> eth_keys.keys.Signature:
return self.private_key.sign_msg(data)

def sign_tx(self, tx: NeonTxModel) -> bytes:
tx_dict = tx.to_eth_dict()
tx_dict["chainId"] = self._chain_id
signed_tx = eth_account.Account.sign_transaction(tx_dict, self.private_key)
return bytes(signed_tx.raw_transaction)

def __str__(self) -> str:
return self.to_string()

Expand Down
10 changes: 10 additions & 0 deletions common/neon/transaction_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ def to_rlp_tx(self) -> bytes:

return tx.to_bytes()

def to_eth_dict(self) -> dict:
return dict(
nonce=self.nonce,
gasPrice=self.gas_price,
gas=self.gas_limit,
to=self.to_address.to_checksum(),
value=self.value,
data=self.call_data.to_string(),
)

@property
def has_chain_id(self) -> bool:
return self.chain_id is not None
Expand Down
2 changes: 1 addition & 1 deletion common/neon_rpc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ async def get_neon_account(self, account: NeonAccount, block: NeonBlockHdrModel
acct_list = await self.get_neon_account_list([account], block)
return acct_list[0]

async def get_state_tx_cnt(self, account: NeonAccount, block: NeonBlockHdrModel | None) -> int:
async def get_state_tx_cnt(self, account: NeonAccount, block: NeonBlockHdrModel | None = None) -> int:
acct = await self.get_neon_account(account, block)
return acct.state_tx_cnt

Expand Down
16 changes: 8 additions & 8 deletions proxy/base/server.py → proxy/base/intl_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from common.utils.process_pool import ProcessPool


class BaseProxyComponent:
def __init__(self, server: BaseProxyServer):
class BaseIntlProxyComponent:
def __init__(self, server: BaseIntlProxyServer):
self._server = server

@cached_property
Expand All @@ -32,17 +32,17 @@ def _msg_filter(self) -> LogMsgFilter:
return self._server._msg_filter # noqa


class BaseProxyApi(BaseProxyComponent, AppDataApi):
def __init__(self, server: BaseProxyServer) -> None:
class BaseProxyApi(BaseIntlProxyComponent, AppDataApi):
def __init__(self, server: BaseIntlProxyServer) -> None:
AppDataApi.__init__(self)
BaseProxyComponent.__init__(self, server)
BaseIntlProxyComponent.__init__(self, server)


class BaseProxyServer(AppDataServer):
class BaseIntlProxyServer(AppDataServer):
class _ProcessPool(ProcessPool):
def __init__(self, server: BaseProxyServer) -> None:
def __init__(self, server: BaseIntlProxyServer) -> None:
super().__init__()
self._server: BaseProxyServer | None = server
self._server: BaseIntlProxyServer | None = server

def _on_process_start(self, idx: int) -> None:
self._server._on_process_start(idx)
Expand Down
9 changes: 5 additions & 4 deletions proxy/base/mp_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def neon_tx_hash(self) -> EthTxHash:

@cached_property
def tx_id(self) -> str:
return self.neon_tx_hash.to_bytes()[:4].hex()
return self.neon_tx_hash.ident

@property
def sender(self) -> EthAddress:
Expand Down Expand Up @@ -113,7 +113,7 @@ def to_string(self) -> str:

@cached_property
def tx_id(self) -> str:
return self.neon_tx_hash.to_bytes()[:4].hex()
return self.neon_tx_hash.ident

@property
def process_time_nsec(self) -> int:
Expand Down Expand Up @@ -166,6 +166,7 @@ def is_empty(self) -> bool:

class MpRequest(BaseModel):
ctx_id: str
chain_id: int


class MpTxCntRequest(BaseModel):
Expand Down Expand Up @@ -221,5 +222,5 @@ class MpGetTxResp(BaseModel):


class MpTxPoolContentResp(BaseModel):
pending_list: tuple[NeonTxModel, ...]
queued_list: tuple[NeonTxModel, ...]
pending_list: list[NeonTxModel]
queued_list: list[NeonTxModel]
4 changes: 2 additions & 2 deletions proxy/base/mp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ async def get_tx_by_sender_nonce(self, ctx_id: str, sender: NeonAccount, tx_nonc
resp = await self._get_tx_by_sender_nonce(req)
return resp.tx

async def get_content(self, ctx_id: str) -> MpTxPoolContentResp:
return await self._get_content(MpRequest(ctx_id=ctx_id))
async def get_content(self, ctx_id: str, chain_id: int) -> MpTxPoolContentResp:
return await self._get_content(MpRequest(ctx_id=ctx_id, chain_id=chain_id))

@AppDataClient.method(name="getGasPrice")
async def _get_gas_price(self) -> MpGasPriceModel: ...
Expand Down
24 changes: 24 additions & 0 deletions proxy/base/op_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from typing_extensions import Self, ClassVar

from common.ethereum.bin_str import EthBinStrField
from common.ethereum.hash import EthAddressField, EthAddress
from common.neon.transaction_model import NeonTxModel
from common.solana.pubkey import SolPubKey, SolPubKeyField
from common.solana.transaction_model import SolTxModel
from common.utils.cached import cached_method
Expand Down Expand Up @@ -79,6 +81,28 @@ class OpTokenSolAddressModel(BaseModel):
token_sol_address: SolPubKeyField


class OpSignEthMsgRequest(BaseModel):
req_id: dict
sender: EthAddressField
data: EthBinStrField


class OpSignEthMsgResp(BaseModel):
signed_msg: EthBinStrField
error: str | None = None


class OpSignEthTxRequest(BaseModel):
req_id: dict
neon_tx: NeonTxModel
chain_id: int


class OpSignEthTxResp(BaseModel):
signed_tx: EthBinStrField
error: str | None = None


class OpSignSolTxListRequest(BaseModel):
req_id: dict
owner: SolPubKeyField
Expand Down
21 changes: 21 additions & 0 deletions proxy/base/op_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from typing import Sequence

from common.app_data.client import AppDataClient
from common.ethereum.bin_str import EthBinStrField
from common.ethereum.hash import EthAddressField
from common.neon.transaction_model import NeonTxModel
from common.solana.pubkey import SolPubKey
from common.solana.transaction import SolTx
from common.solana.transaction_model import SolTxModel
Expand All @@ -14,6 +17,10 @@
OpResourceResp,
OpTokenSolAddressModel,
OpGetTokenSolAddressRequest,
OpSignEthMsgRequest,
OpSignEthMsgResp,
OpSignEthTxRequest,
OpSignEthTxResp,
OpSignSolTxListRequest,
OpSolTxListResp,
OpGetSignerKeyListRequest,
Expand Down Expand Up @@ -48,6 +55,14 @@ async def get_token_sol_address(self, req_id: dict, owner: SolPubKey, chain_id:
resp = await self._get_token_sol_address(req)
return resp.token_sol_address

async def sign_eth_msg(self, req_id: dict, sender: EthAddressField, data: EthBinStrField) -> OpSignEthMsgResp:
req = OpSignEthMsgRequest(req_id=req_id, sender=sender, data=data)
return await self._sign_eth_msg(req)

async def sign_eth_tx(self, req_id: dict, neon_tx: NeonTxModel, chain_id: int) -> OpSignEthTxResp:
req = OpSignEthTxRequest(req_id=req_id, neon_tx=neon_tx, chain_id=chain_id)
return await self._sign_eth_tx(req)

async def sign_sol_tx_list(self, req_id: dict, owner: SolPubKey, tx_list: Sequence[SolTx]) -> tuple[SolTx, ...]:
model_list = [SolTxModel.from_raw(tx) for tx in tx_list]
req = OpSignSolTxListRequest(req_id=req_id, owner=owner, tx_list=model_list)
Expand Down Expand Up @@ -78,6 +93,12 @@ async def _free_resource(self, request: OpFreeResourceRequest) -> OpResourceResp
@AppDataClient.method(name="getOperatorTokenAddress")
async def _get_token_sol_address(self, request: OpGetTokenSolAddressRequest) -> OpTokenSolAddressModel: ...

@AppDataClient.method(name="signEthMessage")
async def _sign_eth_msg(self, request: OpSignEthMsgRequest) -> OpSignEthMsgResp: ...

@AppDataClient.method(name="signEthTransaction")
async def _sign_eth_tx(self, request: OpSignEthTxRequest) -> OpSignEthTxResp: ...

@AppDataClient.method(name="signSolanaTransactionList")
async def _sign_sol_tx_list(self, request: OpSignSolTxListRequest) -> OpSolTxListResp: ...

Expand Down
Loading
Loading