Skip to content

Commit

Permalink
Add Async Geth Personal module (ethereum#2299)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbfreem authored Jan 31, 2022
1 parent eb1051e commit e892434
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 24 deletions.
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

0 comments on commit e892434

Please sign in to comment.