Skip to content

Feature/async geth personal #2299

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

Merged
merged 1 commit into from
Jan 31, 2022
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
12 changes: 11 additions & 1 deletion docs/providers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ AsyncHTTPProvider
... modules={'eth': (AsyncEth,),
... 'net': (AsyncNet,),
... 'geth': (Geth,
... {'txpool': (AsyncGethTxPool,)})
... {'txpool': (AsyncGethTxPool,),
... 'personal': (AsyncGethPersonal,)})
... },
... middlewares=[]) # See supported middleware section below for middleware options

Expand Down Expand Up @@ -438,6 +439,15 @@ Net

Geth
****
- :meth:`web3.geth.personal.ec_recover()`
- :meth:`web3.geth.personal.import_raw_key() <web3.geth.personal.import_raw_key>`
- :meth:`web3.geth.personal.list_accounts() <web3.geth.personal.list_accounts>`
- :meth:`web3.geth.personal.list_wallets() <web3.geth.personal.list_wallets()>`
- :meth:`web3.geth.personal.lock_account() <web3.geth.personal.lock_account>`
- :meth:`web3.geth.personal.new_account() <web3.geth.personal.new_account>`
- :meth:`web3.geth.personal.send_transaction() <web3.geth.personal.send_transaction>`
- :meth:`web3.geth.personal.sign()`
- :meth:`web3.geth.personal.unlock_account() <web3.geth.personal.unlock_account>`
- :meth:`web3.geth.txpool.inspect() <web3.geth.txpool.TxPool.inspect()>`
- :meth:`web3.geth.txpool.content() <web3.geth.txpool.TxPool.content()>`
- :meth:`web3.geth.txpool.status() <web3.geth.txpool.TxPool.status()>`
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2299.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added Async functions for Geth Personal module
15 changes: 12 additions & 3 deletions tests/integration/go_ethereum/test_goethereum_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
get_open_port,
)
from web3 import Web3
from web3._utils.module_testing.go_ethereum_personal_module import (
GoEthereumAsyncPersonalModuleTest,
)
from web3.eth import (
AsyncEth,
)
from web3.geth import (
AsyncGethPersonal,
AsyncGethTxPool,
Geth,
)
Expand Down Expand Up @@ -94,10 +98,11 @@ async def async_w3(geth_process, endpoint_uri):
async_gas_price_strategy_middleware,
async_buffered_gas_estimate_middleware
],
modules={'eth': (AsyncEth,),
'async_net': (AsyncNet,),
modules={'eth': AsyncEth,
'async_net': AsyncNet,
'geth': (Geth,
{'txpool': (AsyncGethTxPool,)}
{'txpool': (AsyncGethTxPool,),
'personal': (AsyncGethPersonal,)}
)
}
)
Expand Down Expand Up @@ -144,6 +149,10 @@ class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest):
pass


class TestGoEthereumAsyncPersonalModuleTest(GoEthereumAsyncPersonalModuleTest):
pass


class TestGoEthereumAsyncEthModuleTest(GoEthereumAsyncEthModuleTest):
pass

Expand Down
2 changes: 1 addition & 1 deletion web3/_utils/module_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
AsyncNetModuleTest,
NetModuleTest,
)
from .personal_module import ( # noqa: F401
from .go_ethereum_personal_module import ( # noqa: F401
GoEthereumPersonalModuleTest,
)
from .version_module import ( # noqa: F401
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from web3 import (
constants,
)
from web3.datastructures import (
AttributeDict,
)
from web3.types import ( # noqa: F401
TxParams,
Wei,
Expand All @@ -31,6 +34,7 @@

PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f933673458f'
SECOND_PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f9336712345'
THIRD_PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f9336754321'
PASSWORD = 'web3-testing'
ADDRESS = '0x844B417c0C58B02c2224306047B9fb0D3264fE8c'
SECOND_ADDRESS = '0xB96b6B21053e67BA59907E252D990C71742c41B8'
Expand Down Expand Up @@ -343,3 +347,80 @@ def test_personal_sign_typed_data_deprecated(
)
assert signature == expected_signature
assert len(signature) == 32 + 32 + 1


class GoEthereumAsyncPersonalModuleTest:

@pytest.mark.asyncio
async def test_async_sign_and_ec_recover(self,
async_w3: "Web3",
unlockable_account_dual_type: ChecksumAddress,
unlockable_account_pw: str) -> None:
message = "This is a test"
signature = await async_w3.geth.personal.sign(message, # type: ignore
unlockable_account_dual_type,
unlockable_account_pw)
address = await async_w3.geth.personal.ec_recover(message, signature) # type: ignore
assert is_same_address(unlockable_account_dual_type, address)

@pytest.mark.asyncio
async def test_async_import_key(self, async_w3: "Web3") -> None:
address = await async_w3.geth.personal.import_raw_key(THIRD_PRIVATE_KEY_HEX, # type: ignore
"Testing")
assert address is not None

@pytest.mark.asyncio
async def test_async_list_accounts(self, async_w3: "Web3") -> None:
accounts = await async_w3.geth.personal.list_accounts() # type: ignore
assert len(accounts) > 0

@pytest.mark.asyncio
async def test_async_list_wallets(self, async_w3: "Web3") -> None:
wallets = await async_w3.geth.personal.list_wallets() # type: ignore
assert isinstance(wallets[0], AttributeDict)

@pytest.mark.asyncio
async def test_async_new_account(self, async_w3: "Web3") -> None:
passphrase = "Create New Account"
account = await async_w3.geth.personal.new_account(passphrase) # type: ignore
assert is_checksum_address(account)

@pytest.mark.asyncio
async def test_async_unlock_lock_account(self,
async_w3: "Web3",
unlockable_account_dual_type: ChecksumAddress,
unlockable_account_pw: str) -> None:
unlocked = await async_w3.geth.personal.unlock_account( # type: ignore
unlockable_account_dual_type,
unlockable_account_pw)
assert unlocked is True
locked = await async_w3.geth.personal.lock_account( # type: ignore
unlockable_account_dual_type)
assert locked is True

@pytest.mark.asyncio
async def test_async_send_transaction(self,
async_w3: "Web3",
unlockable_account_dual_type: ChecksumAddress,
unlockable_account_pw: str) -> None:
tx_params = TxParams()
tx_params["to"] = unlockable_account_dual_type
tx_params["from"] = unlockable_account_dual_type
tx_params["value"] = Wei(123)
response = await async_w3.geth.personal.send_transaction( # type: ignore
tx_params,
unlockable_account_pw)
assert response is not None

@pytest.mark.xfail(reason="personal_signTypedData JSON RPC call has not been released in geth")
@pytest.mark.asyncio
async def test_async_sign_typed_data(self,
async_w3: "Web3",
unlockable_account_dual_type: ChecksumAddress,
unlockable_account_pw: str) -> None:
message = {"message": "This is a test"}
signature = await async_w3.geth.personal.sign_typed_data(message, # type: ignore
unlockable_account_dual_type,
unlockable_account_pw)
address = await async_w3.geth.personal.ec_recover(message, signature) # type: ignore
assert is_same_address(unlockable_account_dual_type, address)
166 changes: 147 additions & 19 deletions web3/geth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
from typing import (
Any,
Awaitable,
Dict,
List,
Optional,
)

from eth_typing.encoding import (
HexStr,
)
from eth_typing.evm import (
ChecksumAddress,
)
from hexbytes.main import (
HexBytes,
)

from web3._utils.admin import (
Expand Down Expand Up @@ -64,35 +77,150 @@
Module,
)
from web3.types import (
GethWallet,
TxParams,
TxPoolContent,
TxPoolInspect,
TxPoolStatus,
)


class GethPersonal(Module):
class BaseGethPersonal(Module):
"""
https://github.com/ethereum/go-ethereum/wiki/management-apis#personal
"""
ec_recover = ec_recover
import_raw_key = import_raw_key
list_accounts = list_accounts
list_wallets = list_wallets
lock_account = lock_account
new_account = new_account
send_transaction = send_transaction
sign = sign
sign_typed_data = sign_typed_data
unlock_account = unlock_account
_ec_recover = ec_recover
_import_raw_key = import_raw_key
_list_accounts = list_accounts
_list_wallets = list_wallets
_lock_account = lock_account
_new_account = new_account
_send_transaction = send_transaction
_sign = sign
_sign_typed_data = sign_typed_data
_unlock_account = unlock_account
# deprecated
ecRecover = ecRecover
importRawKey = importRawKey
listAccounts = listAccounts
lockAccount = lockAccount
newAccount = newAccount
sendTransaction = sendTransaction
signTypedData = signTypedData
unlockAccount = unlockAccount
_ecRecover = ecRecover
_importRawKey = importRawKey
_listAccounts = listAccounts
_lockAccount = lockAccount
_newAccount = newAccount
_sendTransaction = sendTransaction
_signTypedData = signTypedData
_unlockAccount = unlockAccount


class GethPersonal(BaseGethPersonal):
is_async = False

def ec_recover(self, message: str, signature: HexStr) -> ChecksumAddress:
return self._ec_recover(message, signature)

def import_raw_key(self, private_key: str, passphrase: str) -> ChecksumAddress:
return self._import_raw_key(private_key, passphrase)

def list_accounts(self) -> List[ChecksumAddress]:
return self._list_accounts()

def list_wallets(self) -> List[GethWallet]:
return self._list_wallets()

def lock_account(self, account: ChecksumAddress) -> bool:
return self._lock_account(account)

def new_account(self, passphrase: str) -> ChecksumAddress:
return self._new_account(passphrase)

def send_transaction(self, transaction: TxParams, passphrase: str) -> HexBytes:
return self._send_transaction(transaction, passphrase)

def sign(self, message: str, account: ChecksumAddress, password: Optional[str]) -> HexStr:
return self._sign(message, account, password)

def sign_typed_data(self,
message: Dict[str, Any],
account: ChecksumAddress,
password: Optional[str]) -> HexStr:
return self._sign_typed_data(message, account, password)

def unlock_account(self,
account: ChecksumAddress,
passphrase: str,
duration: Optional[int] = None) -> bool:
return self._unlock_account(account, passphrase, duration)

def ecRecover(self, message: str, signature: HexStr) -> ChecksumAddress:
return self._ecRecover(message, signature)

def importRawKey(self, private_key: str, passphrase: str) -> ChecksumAddress:
return self._importRawKey(private_key, passphrase)

def listAccounts(self) -> List[ChecksumAddress]:
return self._listAccounts()

def lockAccount(self, account: ChecksumAddress) -> bool:
return self._lockAccount(account)

def newAccount(self, passphrase: str) -> ChecksumAddress:
return self._newAccount(passphrase)

def sendTransaction(self, transaction: TxParams, passphrase: str) -> HexBytes:
return self._sendTransaction(transaction, passphrase)

def signTypedData(self,
message: Dict[str, Any],
account: ChecksumAddress,
password: Optional[str] = None) -> HexStr:
return self._signTypedData(message, account, password)

def unlockAccount(self,
account: ChecksumAddress,
passphrase: str,
duration: Optional[int] = None) -> bool:
return self._unlockAccount(account, passphrase, duration)


class AsyncGethPersonal(BaseGethPersonal):
is_async = True

async def ec_recover(self, message: str, signature: HexStr) -> Awaitable[ChecksumAddress]:
return await self._ec_recover(message, signature) # type: ignore

async def import_raw_key(self, private_key: str, passphrase: str) -> Awaitable[ChecksumAddress]:
return await self._import_raw_key(private_key, passphrase) # type: ignore

async def list_accounts(self) -> Awaitable[List[ChecksumAddress]]:
return await self._list_accounts() # type: ignore

async def list_wallets(self) -> Awaitable[List[GethWallet]]:
return await self._list_wallets() # type: ignore

async def lock_account(self, account: ChecksumAddress) -> Awaitable[bool]:
return await self._lock_account(account) # type: ignore

async def new_account(self, passphrase: str) -> Awaitable[ChecksumAddress]:
return await self._new_account(passphrase) # type: ignore

async def send_transaction(self, transaction: TxParams, passphrase: str) -> Awaitable[HexBytes]:
return await self._send_transaction(transaction, passphrase) # type: ignore

async def sign(self,
message: str,
account: ChecksumAddress,
password: Optional[str]) -> Awaitable[HexStr]:
return await self._sign(message, account, password) # type: ignore

async def sign_typed_data(self,
message: Dict[str, Any],
account: ChecksumAddress,
password: Optional[str]) -> Awaitable[HexStr]:
return await self._sign_typed_data(message, account, password) # type: ignore

async def unlock_account(self,
account: ChecksumAddress,
passphrase: str,
duration: Optional[int] = None) -> Awaitable[bool]:
return await self._unlock_account(account, passphrase, duration) # type: ignore


class BaseTxPool(Module):
Expand Down