Skip to content

Commit e892434

Browse files
authored
Add Async Geth Personal module (#2299)
1 parent eb1051e commit e892434

File tree

6 files changed

+253
-24
lines changed

6 files changed

+253
-24
lines changed

docs/providers.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ AsyncHTTPProvider
392392
... modules={'eth': (AsyncEth,),
393393
... 'net': (AsyncNet,),
394394
... 'geth': (Geth,
395-
... {'txpool': (AsyncGethTxPool,)})
395+
... {'txpool': (AsyncGethTxPool,),
396+
... 'personal': (AsyncGethPersonal,)})
396397
... },
397398
... middlewares=[]) # See supported middleware section below for middleware options
398399
@@ -438,6 +439,15 @@ Net
438439

439440
Geth
440441
****
442+
- :meth:`web3.geth.personal.ec_recover()`
443+
- :meth:`web3.geth.personal.import_raw_key() <web3.geth.personal.import_raw_key>`
444+
- :meth:`web3.geth.personal.list_accounts() <web3.geth.personal.list_accounts>`
445+
- :meth:`web3.geth.personal.list_wallets() <web3.geth.personal.list_wallets()>`
446+
- :meth:`web3.geth.personal.lock_account() <web3.geth.personal.lock_account>`
447+
- :meth:`web3.geth.personal.new_account() <web3.geth.personal.new_account>`
448+
- :meth:`web3.geth.personal.send_transaction() <web3.geth.personal.send_transaction>`
449+
- :meth:`web3.geth.personal.sign()`
450+
- :meth:`web3.geth.personal.unlock_account() <web3.geth.personal.unlock_account>`
441451
- :meth:`web3.geth.txpool.inspect() <web3.geth.txpool.TxPool.inspect()>`
442452
- :meth:`web3.geth.txpool.content() <web3.geth.txpool.TxPool.content()>`
443453
- :meth:`web3.geth.txpool.status() <web3.geth.txpool.TxPool.status()>`

newsfragments/2299.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added Async functions for Geth Personal module

tests/integration/go_ethereum/test_goethereum_http.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
get_open_port,
55
)
66
from web3 import Web3
7+
from web3._utils.module_testing.go_ethereum_personal_module import (
8+
GoEthereumAsyncPersonalModuleTest,
9+
)
710
from web3.eth import (
811
AsyncEth,
912
)
1013
from web3.geth import (
14+
AsyncGethPersonal,
1115
AsyncGethTxPool,
1216
Geth,
1317
)
@@ -94,10 +98,11 @@ async def async_w3(geth_process, endpoint_uri):
9498
async_gas_price_strategy_middleware,
9599
async_buffered_gas_estimate_middleware
96100
],
97-
modules={'eth': (AsyncEth,),
98-
'async_net': (AsyncNet,),
101+
modules={'eth': AsyncEth,
102+
'async_net': AsyncNet,
99103
'geth': (Geth,
100-
{'txpool': (AsyncGethTxPool,)}
104+
{'txpool': (AsyncGethTxPool,),
105+
'personal': (AsyncGethPersonal,)}
101106
)
102107
}
103108
)
@@ -144,6 +149,10 @@ class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest):
144149
pass
145150

146151

152+
class TestGoEthereumAsyncPersonalModuleTest(GoEthereumAsyncPersonalModuleTest):
153+
pass
154+
155+
147156
class TestGoEthereumAsyncEthModuleTest(GoEthereumAsyncEthModuleTest):
148157
pass
149158

web3/_utils/module_testing/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
AsyncNetModuleTest,
1414
NetModuleTest,
1515
)
16-
from .personal_module import ( # noqa: F401
16+
from .go_ethereum_personal_module import ( # noqa: F401
1717
GoEthereumPersonalModuleTest,
1818
)
1919
from .version_module import ( # noqa: F401

web3/_utils/module_testing/personal_module.py renamed to web3/_utils/module_testing/go_ethereum_personal_module.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
from web3 import (
2222
constants,
2323
)
24+
from web3.datastructures import (
25+
AttributeDict,
26+
)
2427
from web3.types import ( # noqa: F401
2528
TxParams,
2629
Wei,
@@ -31,6 +34,7 @@
3134

3235
PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f933673458f'
3336
SECOND_PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f9336712345'
37+
THIRD_PRIVATE_KEY_HEX = '0x56ebb41875ceedd42e395f730e03b5c44989393c9f0484ee6bc05f9336754321'
3438
PASSWORD = 'web3-testing'
3539
ADDRESS = '0x844B417c0C58B02c2224306047B9fb0D3264fE8c'
3640
SECOND_ADDRESS = '0xB96b6B21053e67BA59907E252D990C71742c41B8'
@@ -343,3 +347,80 @@ def test_personal_sign_typed_data_deprecated(
343347
)
344348
assert signature == expected_signature
345349
assert len(signature) == 32 + 32 + 1
350+
351+
352+
class GoEthereumAsyncPersonalModuleTest:
353+
354+
@pytest.mark.asyncio
355+
async def test_async_sign_and_ec_recover(self,
356+
async_w3: "Web3",
357+
unlockable_account_dual_type: ChecksumAddress,
358+
unlockable_account_pw: str) -> None:
359+
message = "This is a test"
360+
signature = await async_w3.geth.personal.sign(message, # type: ignore
361+
unlockable_account_dual_type,
362+
unlockable_account_pw)
363+
address = await async_w3.geth.personal.ec_recover(message, signature) # type: ignore
364+
assert is_same_address(unlockable_account_dual_type, address)
365+
366+
@pytest.mark.asyncio
367+
async def test_async_import_key(self, async_w3: "Web3") -> None:
368+
address = await async_w3.geth.personal.import_raw_key(THIRD_PRIVATE_KEY_HEX, # type: ignore
369+
"Testing")
370+
assert address is not None
371+
372+
@pytest.mark.asyncio
373+
async def test_async_list_accounts(self, async_w3: "Web3") -> None:
374+
accounts = await async_w3.geth.personal.list_accounts() # type: ignore
375+
assert len(accounts) > 0
376+
377+
@pytest.mark.asyncio
378+
async def test_async_list_wallets(self, async_w3: "Web3") -> None:
379+
wallets = await async_w3.geth.personal.list_wallets() # type: ignore
380+
assert isinstance(wallets[0], AttributeDict)
381+
382+
@pytest.mark.asyncio
383+
async def test_async_new_account(self, async_w3: "Web3") -> None:
384+
passphrase = "Create New Account"
385+
account = await async_w3.geth.personal.new_account(passphrase) # type: ignore
386+
assert is_checksum_address(account)
387+
388+
@pytest.mark.asyncio
389+
async def test_async_unlock_lock_account(self,
390+
async_w3: "Web3",
391+
unlockable_account_dual_type: ChecksumAddress,
392+
unlockable_account_pw: str) -> None:
393+
unlocked = await async_w3.geth.personal.unlock_account( # type: ignore
394+
unlockable_account_dual_type,
395+
unlockable_account_pw)
396+
assert unlocked is True
397+
locked = await async_w3.geth.personal.lock_account( # type: ignore
398+
unlockable_account_dual_type)
399+
assert locked is True
400+
401+
@pytest.mark.asyncio
402+
async def test_async_send_transaction(self,
403+
async_w3: "Web3",
404+
unlockable_account_dual_type: ChecksumAddress,
405+
unlockable_account_pw: str) -> None:
406+
tx_params = TxParams()
407+
tx_params["to"] = unlockable_account_dual_type
408+
tx_params["from"] = unlockable_account_dual_type
409+
tx_params["value"] = Wei(123)
410+
response = await async_w3.geth.personal.send_transaction( # type: ignore
411+
tx_params,
412+
unlockable_account_pw)
413+
assert response is not None
414+
415+
@pytest.mark.xfail(reason="personal_signTypedData JSON RPC call has not been released in geth")
416+
@pytest.mark.asyncio
417+
async def test_async_sign_typed_data(self,
418+
async_w3: "Web3",
419+
unlockable_account_dual_type: ChecksumAddress,
420+
unlockable_account_pw: str) -> None:
421+
message = {"message": "This is a test"}
422+
signature = await async_w3.geth.personal.sign_typed_data(message, # type: ignore
423+
unlockable_account_dual_type,
424+
unlockable_account_pw)
425+
address = await async_w3.geth.personal.ec_recover(message, signature) # type: ignore
426+
assert is_same_address(unlockable_account_dual_type, address)

web3/geth.py

Lines changed: 147 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
from typing import (
22
Any,
33
Awaitable,
4+
Dict,
5+
List,
6+
Optional,
7+
)
8+
9+
from eth_typing.encoding import (
10+
HexStr,
11+
)
12+
from eth_typing.evm import (
13+
ChecksumAddress,
14+
)
15+
from hexbytes.main import (
16+
HexBytes,
417
)
518

619
from web3._utils.admin import (
@@ -64,35 +77,150 @@
6477
Module,
6578
)
6679
from web3.types import (
80+
GethWallet,
81+
TxParams,
6782
TxPoolContent,
6883
TxPoolInspect,
6984
TxPoolStatus,
7085
)
7186

7287

73-
class GethPersonal(Module):
88+
class BaseGethPersonal(Module):
7489
"""
7590
https://github.com/ethereum/go-ethereum/wiki/management-apis#personal
7691
"""
77-
ec_recover = ec_recover
78-
import_raw_key = import_raw_key
79-
list_accounts = list_accounts
80-
list_wallets = list_wallets
81-
lock_account = lock_account
82-
new_account = new_account
83-
send_transaction = send_transaction
84-
sign = sign
85-
sign_typed_data = sign_typed_data
86-
unlock_account = unlock_account
92+
_ec_recover = ec_recover
93+
_import_raw_key = import_raw_key
94+
_list_accounts = list_accounts
95+
_list_wallets = list_wallets
96+
_lock_account = lock_account
97+
_new_account = new_account
98+
_send_transaction = send_transaction
99+
_sign = sign
100+
_sign_typed_data = sign_typed_data
101+
_unlock_account = unlock_account
87102
# deprecated
88-
ecRecover = ecRecover
89-
importRawKey = importRawKey
90-
listAccounts = listAccounts
91-
lockAccount = lockAccount
92-
newAccount = newAccount
93-
sendTransaction = sendTransaction
94-
signTypedData = signTypedData
95-
unlockAccount = unlockAccount
103+
_ecRecover = ecRecover
104+
_importRawKey = importRawKey
105+
_listAccounts = listAccounts
106+
_lockAccount = lockAccount
107+
_newAccount = newAccount
108+
_sendTransaction = sendTransaction
109+
_signTypedData = signTypedData
110+
_unlockAccount = unlockAccount
111+
112+
113+
class GethPersonal(BaseGethPersonal):
114+
is_async = False
115+
116+
def ec_recover(self, message: str, signature: HexStr) -> ChecksumAddress:
117+
return self._ec_recover(message, signature)
118+
119+
def import_raw_key(self, private_key: str, passphrase: str) -> ChecksumAddress:
120+
return self._import_raw_key(private_key, passphrase)
121+
122+
def list_accounts(self) -> List[ChecksumAddress]:
123+
return self._list_accounts()
124+
125+
def list_wallets(self) -> List[GethWallet]:
126+
return self._list_wallets()
127+
128+
def lock_account(self, account: ChecksumAddress) -> bool:
129+
return self._lock_account(account)
130+
131+
def new_account(self, passphrase: str) -> ChecksumAddress:
132+
return self._new_account(passphrase)
133+
134+
def send_transaction(self, transaction: TxParams, passphrase: str) -> HexBytes:
135+
return self._send_transaction(transaction, passphrase)
136+
137+
def sign(self, message: str, account: ChecksumAddress, password: Optional[str]) -> HexStr:
138+
return self._sign(message, account, password)
139+
140+
def sign_typed_data(self,
141+
message: Dict[str, Any],
142+
account: ChecksumAddress,
143+
password: Optional[str]) -> HexStr:
144+
return self._sign_typed_data(message, account, password)
145+
146+
def unlock_account(self,
147+
account: ChecksumAddress,
148+
passphrase: str,
149+
duration: Optional[int] = None) -> bool:
150+
return self._unlock_account(account, passphrase, duration)
151+
152+
def ecRecover(self, message: str, signature: HexStr) -> ChecksumAddress:
153+
return self._ecRecover(message, signature)
154+
155+
def importRawKey(self, private_key: str, passphrase: str) -> ChecksumAddress:
156+
return self._importRawKey(private_key, passphrase)
157+
158+
def listAccounts(self) -> List[ChecksumAddress]:
159+
return self._listAccounts()
160+
161+
def lockAccount(self, account: ChecksumAddress) -> bool:
162+
return self._lockAccount(account)
163+
164+
def newAccount(self, passphrase: str) -> ChecksumAddress:
165+
return self._newAccount(passphrase)
166+
167+
def sendTransaction(self, transaction: TxParams, passphrase: str) -> HexBytes:
168+
return self._sendTransaction(transaction, passphrase)
169+
170+
def signTypedData(self,
171+
message: Dict[str, Any],
172+
account: ChecksumAddress,
173+
password: Optional[str] = None) -> HexStr:
174+
return self._signTypedData(message, account, password)
175+
176+
def unlockAccount(self,
177+
account: ChecksumAddress,
178+
passphrase: str,
179+
duration: Optional[int] = None) -> bool:
180+
return self._unlockAccount(account, passphrase, duration)
181+
182+
183+
class AsyncGethPersonal(BaseGethPersonal):
184+
is_async = True
185+
186+
async def ec_recover(self, message: str, signature: HexStr) -> Awaitable[ChecksumAddress]:
187+
return await self._ec_recover(message, signature) # type: ignore
188+
189+
async def import_raw_key(self, private_key: str, passphrase: str) -> Awaitable[ChecksumAddress]:
190+
return await self._import_raw_key(private_key, passphrase) # type: ignore
191+
192+
async def list_accounts(self) -> Awaitable[List[ChecksumAddress]]:
193+
return await self._list_accounts() # type: ignore
194+
195+
async def list_wallets(self) -> Awaitable[List[GethWallet]]:
196+
return await self._list_wallets() # type: ignore
197+
198+
async def lock_account(self, account: ChecksumAddress) -> Awaitable[bool]:
199+
return await self._lock_account(account) # type: ignore
200+
201+
async def new_account(self, passphrase: str) -> Awaitable[ChecksumAddress]:
202+
return await self._new_account(passphrase) # type: ignore
203+
204+
async def send_transaction(self, transaction: TxParams, passphrase: str) -> Awaitable[HexBytes]:
205+
return await self._send_transaction(transaction, passphrase) # type: ignore
206+
207+
async def sign(self,
208+
message: str,
209+
account: ChecksumAddress,
210+
password: Optional[str]) -> Awaitable[HexStr]:
211+
return await self._sign(message, account, password) # type: ignore
212+
213+
async def sign_typed_data(self,
214+
message: Dict[str, Any],
215+
account: ChecksumAddress,
216+
password: Optional[str]) -> Awaitable[HexStr]:
217+
return await self._sign_typed_data(message, account, password) # type: ignore
218+
219+
async def unlock_account(self,
220+
account: ChecksumAddress,
221+
passphrase: str,
222+
duration: Optional[int] = None) -> Awaitable[bool]:
223+
return await self._unlock_account(account, passphrase, duration) # type: ignore
96224

97225

98226
class BaseTxPool(Module):

0 commit comments

Comments
 (0)