py-near 1.1.52__py3-none-any.whl → 1.1.53__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py_near-1.1.52.dist-info → py_near-1.1.53.dist-info}/METADATA +3 -5
- {py_near-1.1.52.dist-info → py_near-1.1.53.dist-info}/RECORD +4 -13
- {py_near-1.1.52.dist-info → py_near-1.1.53.dist-info}/WHEEL +1 -1
- py_near/mpc/__init__.py +0 -0
- py_near/mpc/auth/__init__.py +0 -0
- py_near/mpc/auth/auth_2fa.py +0 -97
- py_near/mpc/auth/base.py +0 -23
- py_near/mpc/auth/core.py +0 -0
- py_near/mpc/auth/default_auth.py +0 -29
- py_near/mpc/auth/keys_auth.py +0 -83
- py_near/mpc/models.py +0 -32
- py_near/mpc/wallet.py +0 -261
- {py_near-1.1.52.dist-info → py_near-1.1.53.dist-info}/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: py-near
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.53
|
4
4
|
Summary: Pretty simple and fully asynchronous framework for working with NEAR blockchain
|
5
5
|
Author: pvolnov
|
6
6
|
Author-email: petr@herewallet.app
|
@@ -11,12 +11,10 @@ Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Requires-Dist: base58 (>=2.1.1,<3.0.0)
|
14
|
-
Requires-Dist: eth-keys (>=0.6.1,<0.7.0)
|
15
|
-
Requires-Dist: eth-utils (>=5.2.0,<6.0.0)
|
16
14
|
Requires-Dist: frozenlist (>=1.4.1,<2.0.0)
|
17
|
-
Requires-Dist: httpx (
|
15
|
+
Requires-Dist: httpx (>=0.26.0)
|
18
16
|
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
19
|
-
Requires-Dist: py-near-primitives (==0.2.
|
17
|
+
Requires-Dist: py-near-primitives (==0.2.4)
|
20
18
|
Requires-Dist: pydantic (>=2.6.2,<3.0.0)
|
21
19
|
Requires-Dist: pynacl (>=1.5.0,<2.0.0)
|
22
20
|
Description-Content-Type: text/markdown
|
@@ -20,19 +20,10 @@ py_near/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
20
20
|
py_near/exceptions/exceptions.py,sha256=DEFipaAHm0y7oCuN2QKzHsiQvUTUQVl-Ce36Ag7n7hs,5509
|
21
21
|
py_near/exceptions/provider.py,sha256=K-wexgjPJ8sw42JePwaP7R5dJEIn9DoFJRvVcURsx6s,7718
|
22
22
|
py_near/models.py,sha256=85ynmLVJnzWvFKq-Z8iIO0waU-2iP-CSkaOGX4mYoL0,11590
|
23
|
-
py_near/mpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
-
py_near/mpc/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
py_near/mpc/auth/auth_2fa.py,sha256=ZxO8mGO95ixb0qXnKrfmVxP7ivpDKerH4-udV_vUmbs,3124
|
26
|
-
py_near/mpc/auth/base.py,sha256=IoFdRWUQcerYZq8TCkpjXJQRcENnzA9WwFbVPgmHgcs,629
|
27
|
-
py_near/mpc/auth/core.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
py_near/mpc/auth/default_auth.py,sha256=i70412yXYx3MlFSsPxHLKbF0CXTjC39Qtl34et8kAEw,751
|
29
|
-
py_near/mpc/auth/keys_auth.py,sha256=DJPi37DnV6r6cSskHEhHBiR_QiaAQJkg0AEsYp6cePA,2359
|
30
|
-
py_near/mpc/models.py,sha256=9MSfBDqfqkaJu6eJxnwyjgihsexZboehbJ6vAGa4BTU,663
|
31
|
-
py_near/mpc/wallet.py,sha256=sNr9UlcyVDPF_Zho_4iTupbUoZct3paQlxxDHlHK67k,9768
|
32
23
|
py_near/providers.py,sha256=X_GfddFzmafBatqFJH41Dp_lYvTNcz1Q3Ko2tEnN0VU,16193
|
33
24
|
py_near/transactions.py,sha256=QwEP3qOGxb8OB2vZA_rU20jvkPA0FmJowUKDxbuzOuI,3251
|
34
25
|
py_near/utils.py,sha256=FirRH93ydH1cwjn0-sNrZeIn3BRD6QHedrP2VkAdJ6g,126
|
35
|
-
py_near-1.1.
|
36
|
-
py_near-1.1.
|
37
|
-
py_near-1.1.
|
38
|
-
py_near-1.1.
|
26
|
+
py_near-1.1.53.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
|
27
|
+
py_near-1.1.53.dist-info/METADATA,sha256=aE8cvfs6Axphip2sszcwmHLdd1cXmXq1d3W54CjgMe4,4763
|
28
|
+
py_near-1.1.53.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
29
|
+
py_near-1.1.53.dist-info/RECORD,,
|
py_near/mpc/__init__.py
DELETED
File without changes
|
py_near/mpc/auth/__init__.py
DELETED
File without changes
|
py_near/mpc/auth/auth_2fa.py
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
from hashlib import sha256
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
import base58
|
6
|
-
from nacl import signing
|
7
|
-
from pydantic import BaseModel
|
8
|
-
|
9
|
-
from py_near.account import Account
|
10
|
-
from py_near.mpc.auth.base import AuthContract
|
11
|
-
|
12
|
-
|
13
|
-
class Wallet2FA(BaseModel):
|
14
|
-
wallet_id: str
|
15
|
-
public_key: str
|
16
|
-
delay_to_remove: int
|
17
|
-
cancellation_at: int
|
18
|
-
conditions: Optional[str] = None
|
19
|
-
|
20
|
-
@classmethod
|
21
|
-
def build(cls, data: dict, wallet_id):
|
22
|
-
return cls(
|
23
|
-
wallet_id=wallet_id,
|
24
|
-
public_key=data["public_key"],
|
25
|
-
cancellation_at=data["cancellation_at"],
|
26
|
-
delay_to_remove=data["delay_to_remove"],
|
27
|
-
conditions=data.get("conditions"),
|
28
|
-
)
|
29
|
-
|
30
|
-
|
31
|
-
class AuthContract2FA(AuthContract):
|
32
|
-
root_pk: signing.SigningKey
|
33
|
-
contract_id: str = "2fa.auth.hot.tg"
|
34
|
-
|
35
|
-
def __init__(self, root_pk: signing.SigningKey, near_account: Account = None):
|
36
|
-
self.root_pk = root_pk
|
37
|
-
self.near_account = near_account
|
38
|
-
super().__init__()
|
39
|
-
|
40
|
-
def generate_user_payload(self, msg_hash: bytes):
|
41
|
-
auth_signature = base58.b58encode(self.root_pk.sign(msg_hash).signature).decode(
|
42
|
-
"utf-8"
|
43
|
-
)
|
44
|
-
return auth_signature
|
45
|
-
|
46
|
-
async def get_2fa_for_wallet(self, wallet_id: str) -> Optional[Wallet2FA]:
|
47
|
-
if not self.near_account:
|
48
|
-
raise ValueError("Near account is required")
|
49
|
-
res = (
|
50
|
-
await self.near_account.view_function(
|
51
|
-
self.contract_id,
|
52
|
-
"get_wallet_2fa",
|
53
|
-
{
|
54
|
-
"wallet_id": wallet_id,
|
55
|
-
},
|
56
|
-
)
|
57
|
-
).result
|
58
|
-
if res:
|
59
|
-
return Wallet2FA.build(res, wallet_id)
|
60
|
-
|
61
|
-
async def cancel_2fa(
|
62
|
-
self, user_pk: signing.SigningKey, wallet_id: str, rule_id: int = 0
|
63
|
-
):
|
64
|
-
if not self.near_account:
|
65
|
-
raise ValueError("Near account is required")
|
66
|
-
two_fa = await self.get_2fa_for_wallet(wallet_id)
|
67
|
-
ts = int(datetime.datetime.utcnow().timestamp() * 10**9)
|
68
|
-
|
69
|
-
if two_fa.cancellation_at == 0:
|
70
|
-
msg_hash = sha256(f"CANCEL_2FA:{wallet_id}:{ts}".encode("utf-8")).digest()
|
71
|
-
auth_signature = base58.b58encode(user_pk.sign(msg_hash).signature).decode(
|
72
|
-
"utf-8"
|
73
|
-
)
|
74
|
-
return await self.near_account.function_call(
|
75
|
-
self.contract_id,
|
76
|
-
"cancel_2fa",
|
77
|
-
{
|
78
|
-
"wallet_id": wallet_id,
|
79
|
-
"user_signature": auth_signature,
|
80
|
-
"timestamp": ts,
|
81
|
-
},
|
82
|
-
included=True,
|
83
|
-
)
|
84
|
-
else:
|
85
|
-
if two_fa.cancellation_at < ts:
|
86
|
-
raise ValueError(
|
87
|
-
f"2FA already cancelled at {datetime.datetime.fromtimestamp(two_fa.cancellation_at / 10**9)}"
|
88
|
-
)
|
89
|
-
return await self.near_account.function_call(
|
90
|
-
self.contract_id,
|
91
|
-
"complete_cancel_2fa",
|
92
|
-
{
|
93
|
-
"wallet_id": wallet_id,
|
94
|
-
"rule_id": rule_id,
|
95
|
-
},
|
96
|
-
included=True,
|
97
|
-
)
|
py_near/mpc/auth/base.py
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
from py_near.account import Account
|
2
|
-
from py_near.mpc.models import WalletAccessModel
|
3
|
-
|
4
|
-
|
5
|
-
class AuthContract:
|
6
|
-
contract_id: str
|
7
|
-
|
8
|
-
def __init__(self, near_account: Account = None):
|
9
|
-
self.near_account = near_account
|
10
|
-
|
11
|
-
async def grant_access(
|
12
|
-
self,
|
13
|
-
wallet_id: str,
|
14
|
-
access: WalletAccessModel,
|
15
|
-
near_account,
|
16
|
-
wallet_auth_method=0,
|
17
|
-
):
|
18
|
-
raise NotImplementedError("Grant access is not implemented")
|
19
|
-
|
20
|
-
async def revoke_assess(
|
21
|
-
self, wallet_id: str, near_account: Account, access_id: int
|
22
|
-
):
|
23
|
-
raise NotImplementedError("Revoke access is not implemented")
|
py_near/mpc/auth/core.py
DELETED
File without changes
|
py_near/mpc/auth/default_auth.py
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
|
3
|
-
import base58
|
4
|
-
from nacl import signing
|
5
|
-
|
6
|
-
from py_near.mpc.auth.base import AuthContract
|
7
|
-
|
8
|
-
|
9
|
-
class DefaultAuthContract(AuthContract):
|
10
|
-
root_pk: signing.SigningKey
|
11
|
-
contract_id: str = "default.auth.hot.tg"
|
12
|
-
|
13
|
-
def __init__(self, root_pk: signing.SigningKey):
|
14
|
-
self.root_pk = root_pk
|
15
|
-
super().__init__()
|
16
|
-
|
17
|
-
def generate_user_payload(self, msg_hash: bytes):
|
18
|
-
auth_signature = base58.b58encode(self.root_pk.sign(msg_hash).signature).decode(
|
19
|
-
"utf-8"
|
20
|
-
)
|
21
|
-
return json.dumps(
|
22
|
-
dict(
|
23
|
-
signature=auth_signature,
|
24
|
-
public_key=base58.b58encode(
|
25
|
-
bytes(self.root_pk.verify_key.encode())
|
26
|
-
).decode(),
|
27
|
-
)
|
28
|
-
)
|
29
|
-
|
py_near/mpc/auth/keys_auth.py
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from hashlib import sha256
|
3
|
-
from typing import List
|
4
|
-
|
5
|
-
import base58
|
6
|
-
from nacl import signing
|
7
|
-
|
8
|
-
from py_near.account import Account
|
9
|
-
from py_near.mpc.auth.base import AuthContract
|
10
|
-
from py_near.mpc.models import WalletAccessModel
|
11
|
-
|
12
|
-
|
13
|
-
class KeysAuthContract(AuthContract):
|
14
|
-
auth_keys: List[signing.SigningKey]
|
15
|
-
auth_method: int
|
16
|
-
contract_id: str = "keys.auth.hot.tg"
|
17
|
-
|
18
|
-
def __init__(
|
19
|
-
self,
|
20
|
-
auth_keys: List[signing.SigningKey],
|
21
|
-
auth_method=0,
|
22
|
-
near_account: Account = None,
|
23
|
-
):
|
24
|
-
self.auth_keys = auth_keys
|
25
|
-
self.auth_method = auth_method
|
26
|
-
super().__init__(near_account)
|
27
|
-
|
28
|
-
def generate_user_payload(self, msg_hash: bytes):
|
29
|
-
signatures = []
|
30
|
-
for key in self.auth_keys:
|
31
|
-
signatures.append(
|
32
|
-
base58.b58encode(key.sign(msg_hash).signature).decode("utf-8")
|
33
|
-
)
|
34
|
-
return json.dumps(
|
35
|
-
dict(
|
36
|
-
signatures=signatures,
|
37
|
-
auth_method=0,
|
38
|
-
)
|
39
|
-
)
|
40
|
-
|
41
|
-
async def grant_access(
|
42
|
-
self,
|
43
|
-
wallet_id: str,
|
44
|
-
access: WalletAccessModel,
|
45
|
-
near_account,
|
46
|
-
wallet_auth_method=0,
|
47
|
-
):
|
48
|
-
access = access.model_dump()
|
49
|
-
proof_hash = sha256(
|
50
|
-
f"{json.dumps(access).replace(' ', '')}.{wallet_id}".encode("utf-8")
|
51
|
-
).digest()
|
52
|
-
signatures = []
|
53
|
-
for pk in self.auth_keys:
|
54
|
-
signature = base58.b58encode(pk.sign(proof_hash).signature).decode("utf-8")
|
55
|
-
signatures.append(signature)
|
56
|
-
|
57
|
-
res = await near_account.function_call(
|
58
|
-
self.contract_id,
|
59
|
-
"add_wallet_auth_account_id",
|
60
|
-
{
|
61
|
-
"wallet_id": wallet_id,
|
62
|
-
"access": access,
|
63
|
-
"signatures": signatures,
|
64
|
-
"wallet_auth_method": wallet_auth_method,
|
65
|
-
"auth_method": self.auth_method,
|
66
|
-
},
|
67
|
-
included=True,
|
68
|
-
)
|
69
|
-
return res
|
70
|
-
|
71
|
-
async def get_rules(self, wallet_id: str):
|
72
|
-
if not self.near_account:
|
73
|
-
raise ValueError("Near account is required")
|
74
|
-
res = (
|
75
|
-
await self.near_account.view_function(
|
76
|
-
self.contract_id,
|
77
|
-
"get_wallet",
|
78
|
-
{
|
79
|
-
"wallet_id": wallet_id,
|
80
|
-
},
|
81
|
-
)
|
82
|
-
).result
|
83
|
-
return res
|
py_near/mpc/models.py
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
from typing import Optional, List
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
5
|
-
|
6
|
-
|
7
|
-
class CurveType(int, Enum):
|
8
|
-
SECP256K1 = 0
|
9
|
-
ED25519 = 1
|
10
|
-
|
11
|
-
|
12
|
-
_WALLET_REGISTER = "mpc.hot.tg"
|
13
|
-
|
14
|
-
|
15
|
-
class WalletAccessModel(BaseModel):
|
16
|
-
account_id: str
|
17
|
-
metadata: Optional[str] = None
|
18
|
-
chain_id: int
|
19
|
-
msg: Optional[str] = None
|
20
|
-
|
21
|
-
|
22
|
-
class WalletModel(BaseModel):
|
23
|
-
access_list: List[WalletAccessModel]
|
24
|
-
key_gen: int = 0
|
25
|
-
|
26
|
-
@classmethod
|
27
|
-
def build(cls, data: dict):
|
28
|
-
return cls(
|
29
|
-
access_list=[WalletAccessModel(**x) for x in data["access_list"]],
|
30
|
-
block_height=data["block_height"],
|
31
|
-
key_gen=data["key_gen"],
|
32
|
-
)
|
py_near/mpc/wallet.py
DELETED
@@ -1,261 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from hashlib import sha256
|
3
|
-
from typing import List, Optional
|
4
|
-
|
5
|
-
import base58
|
6
|
-
import httpx
|
7
|
-
from eth_keys.datatypes import Signature
|
8
|
-
from eth_keys.exceptions import BadSignature
|
9
|
-
from eth_utils import keccak
|
10
|
-
from loguru import logger
|
11
|
-
from nacl import encoding, signing
|
12
|
-
|
13
|
-
from py_near.account import Account
|
14
|
-
from py_near.mpc.auth.auth_2fa import AuthContract2FA
|
15
|
-
from py_near.mpc.auth.base import AuthContract
|
16
|
-
from py_near.mpc.auth.default_auth import DefaultAuthContract
|
17
|
-
from py_near.mpc.auth.keys_auth import KeysAuthContract
|
18
|
-
from py_near.mpc.models import WalletAccessModel, WalletModel, CurveType
|
19
|
-
|
20
|
-
_WALLET_REGISTER = "mpc.hot.tg"
|
21
|
-
|
22
|
-
AUTH_CLASS = {
|
23
|
-
"default.auth.hot.tg": DefaultAuthContract,
|
24
|
-
"keys.auth.hot.tg": KeysAuthContract,
|
25
|
-
"2fa.auth.hot.tg": AuthContract2FA,
|
26
|
-
}
|
27
|
-
|
28
|
-
|
29
|
-
class MPCWallet:
|
30
|
-
derive: bytes
|
31
|
-
near_account: Account
|
32
|
-
hot_rpc: str
|
33
|
-
|
34
|
-
def __init__(
|
35
|
-
self,
|
36
|
-
near_account: Account,
|
37
|
-
default_root_pk: Optional[bytes] = None,
|
38
|
-
hot_rpc: str = "https://rpc1.hotdao.ai",
|
39
|
-
derive: Optional[bytes] = None,
|
40
|
-
):
|
41
|
-
"""
|
42
|
-
:param near_account: Near account
|
43
|
-
:param derive: Derived key for wallet
|
44
|
-
:param hot_rpc: Hot RPC url
|
45
|
-
:param default_root_pk: Default auth key for HOT Protocol, if provided, derive = sha256(default_root_pk.public_key).digest()
|
46
|
-
"""
|
47
|
-
self.near_account = near_account
|
48
|
-
if len(default_root_pk) != 32:
|
49
|
-
raise ValueError("Default root pk should be 32 bytes")
|
50
|
-
if default_root_pk:
|
51
|
-
self.default_root_pk = default_root_pk
|
52
|
-
private_key_obj = self.derive_private_key(0)
|
53
|
-
derive = sha256(private_key_obj.verify_key.encode()).digest()
|
54
|
-
elif derive is None:
|
55
|
-
logger.warning(
|
56
|
-
"Its recomended to use default_root_pk to generate derives, v0 wallets support will be deprecated"
|
57
|
-
)
|
58
|
-
raise ValueError("Derive is required")
|
59
|
-
self.derive = derive
|
60
|
-
self.hot_rpc = hot_rpc
|
61
|
-
self._client = httpx.AsyncClient()
|
62
|
-
|
63
|
-
def derive_private_key(self, gen=0):
|
64
|
-
private_key = self.default_root_pk
|
65
|
-
for _ in range(gen):
|
66
|
-
private_key = sha256(private_key).digest()
|
67
|
-
return signing.SigningKey(
|
68
|
-
private_key,
|
69
|
-
encoder=encoding.RawEncoder,
|
70
|
-
)
|
71
|
-
|
72
|
-
@property
|
73
|
-
def wallet_id(self):
|
74
|
-
return base58.b58encode(sha256(self.derive).digest()).decode("utf-8")
|
75
|
-
|
76
|
-
async def get_wallet(self):
|
77
|
-
wallet = await self.near_account.view_function(
|
78
|
-
_WALLET_REGISTER, "get_wallet", args={"wallet_id": self.wallet_id}
|
79
|
-
)
|
80
|
-
if wallet.result:
|
81
|
-
return WalletModel.build(wallet.result)
|
82
|
-
|
83
|
-
async def create_wallet_with_keys_auth(self, public_key: bytes, key_gen=1):
|
84
|
-
"""
|
85
|
-
Create wallet with keys.auth.hot.tg auth method.
|
86
|
-
:param public_key: Public key for auth future signs on keys.auth.hot.tg
|
87
|
-
"""
|
88
|
-
wallet = await self.get_wallet()
|
89
|
-
if wallet and wallet.access_list[0].account_id != "default.auth.hot.tg":
|
90
|
-
raise ValueError(
|
91
|
-
"MPCWallet already exists with different auth method, please use another wallet_id"
|
92
|
-
)
|
93
|
-
auth_to_add_msg = json.dumps(
|
94
|
-
dict(public_keys=[base58.b58encode(public_key).decode()], rules=[])
|
95
|
-
).replace(" ", "")
|
96
|
-
return await self.create_wallet(
|
97
|
-
"keys.auth.hot.tg", None, auth_to_add_msg, key_gen
|
98
|
-
)
|
99
|
-
|
100
|
-
async def create_wallet(
|
101
|
-
self, auth_account_id: str, metadata: str = "", auth_to_add_msg="", key_gen=1
|
102
|
-
):
|
103
|
-
"""
|
104
|
-
Create wallet with keys.auth.hot.tg auth method.
|
105
|
-
:param public_key: Public key for auth future signs on keys.auth.hot.tg
|
106
|
-
"""
|
107
|
-
wallet = await self.get_wallet()
|
108
|
-
if wallet and wallet.access_list[0].account_id != "default.auth.hot.tg":
|
109
|
-
raise ValueError(
|
110
|
-
"MPCWallet already exists with different auth method, please use another wallet_id"
|
111
|
-
)
|
112
|
-
root_pk = self.derive_private_key(0)
|
113
|
-
proof_hash = sha256(
|
114
|
-
f"CREATE_WALLET:{self.wallet_id}:{auth_account_id}:{metadata}:{auth_to_add_msg}".encode(
|
115
|
-
"utf-8"
|
116
|
-
)
|
117
|
-
).digest()
|
118
|
-
signature = base58.b58encode(root_pk.sign(proof_hash).signature).decode("utf-8")
|
119
|
-
|
120
|
-
s = await self._client.post(
|
121
|
-
f"{self.hot_rpc}/create_wallet",
|
122
|
-
json=dict(
|
123
|
-
wallet_id=self.wallet_id,
|
124
|
-
key_gen=key_gen,
|
125
|
-
signature=signature,
|
126
|
-
wallet_derive_public_key=base58.b58encode(
|
127
|
-
root_pk.verify_key.encode()
|
128
|
-
).decode(),
|
129
|
-
auth={
|
130
|
-
"auth_account_id": auth_account_id,
|
131
|
-
"msg": auth_to_add_msg,
|
132
|
-
"metadata": metadata or None,
|
133
|
-
},
|
134
|
-
),
|
135
|
-
timeout=30,
|
136
|
-
)
|
137
|
-
return s.json()
|
138
|
-
|
139
|
-
async def get_ecdsa_public_key(self) -> bytes:
|
140
|
-
resp = (
|
141
|
-
await self._client.post(
|
142
|
-
f"{self.hot_rpc}/public_key",
|
143
|
-
json=dict(wallet_derive=base58.b58encode(self.derive).decode()),
|
144
|
-
timeout=10,
|
145
|
-
follow_redirects=True,
|
146
|
-
)
|
147
|
-
).json()
|
148
|
-
return bytes.fromhex(resp["ecdsa"])
|
149
|
-
|
150
|
-
@property
|
151
|
-
def public_key(self):
|
152
|
-
# TODO Calculate with Rust Code
|
153
|
-
raise NotImplementedError("Public key calculation is not implemented yet")
|
154
|
-
|
155
|
-
async def sign_message(
|
156
|
-
self,
|
157
|
-
msg_hash: bytes,
|
158
|
-
message_body: Optional[bytes] = None,
|
159
|
-
curve_type: CurveType = CurveType.SECP256K1,
|
160
|
-
auth_methods: List[AuthContract] = None,
|
161
|
-
):
|
162
|
-
if not self.default_root_pk:
|
163
|
-
raise ValueError("Default auth key is required")
|
164
|
-
wallet = await self.get_wallet()
|
165
|
-
user_payloads = []
|
166
|
-
if len(auth_methods) != len(wallet.access_list):
|
167
|
-
raise ValueError("Auth methods count should be equal to wallet access list")
|
168
|
-
|
169
|
-
for auth_contract, auth_method in zip(auth_methods, wallet.access_list):
|
170
|
-
user_payloads.append(auth_contract.generate_user_payload(msg_hash))
|
171
|
-
|
172
|
-
proof = {
|
173
|
-
"auth_id": 0,
|
174
|
-
"curve_type": curve_type,
|
175
|
-
"user_payloads": user_payloads,
|
176
|
-
"message_body": message_body.hex() if message_body else "",
|
177
|
-
}
|
178
|
-
|
179
|
-
resp = await self._client.post(
|
180
|
-
f"{self.hot_rpc}/sign_raw",
|
181
|
-
json=dict(
|
182
|
-
uid=self.derive.hex(),
|
183
|
-
message=msg_hash.hex(),
|
184
|
-
proof=proof,
|
185
|
-
key_type=curve_type,
|
186
|
-
),
|
187
|
-
timeout=10,
|
188
|
-
follow_redirects=True,
|
189
|
-
)
|
190
|
-
resp = resp.json()
|
191
|
-
if "Ecdsa" not in resp:
|
192
|
-
raise ValueError(f"Invalid response from server: {resp}")
|
193
|
-
resp = resp["Ecdsa"]
|
194
|
-
r = int(resp["big_r"][2:], 16)
|
195
|
-
s = int(resp["signature"], 16)
|
196
|
-
|
197
|
-
pk = await self.get_ecdsa_public_key()
|
198
|
-
for v in (0, 1):
|
199
|
-
sig = Signature(vrs=(v, r, s))
|
200
|
-
recovered = sig.recover_public_key_from_msg_hash(msg_hash)
|
201
|
-
if recovered.to_compressed_bytes() == pk:
|
202
|
-
return (
|
203
|
-
r.to_bytes(32, "big") + s.to_bytes(32, "big") + bytes([v])
|
204
|
-
).hex()
|
205
|
-
|
206
|
-
raise BadSignature("Cannot recover public key from signature")
|
207
|
-
|
208
|
-
async def add_new_access_rule(
|
209
|
-
self, access: WalletAccessModel, auth_contracts: List[AuthContract]
|
210
|
-
):
|
211
|
-
wallet = await self.get_wallet()
|
212
|
-
access_json = access.model_dump_json()
|
213
|
-
message_body = f"ADD_AUTH_METHOD:{access_json}:{self.wallet_id}".encode("utf-8")
|
214
|
-
msg_hash = sha256(message_body).digest()
|
215
|
-
user_payloads = []
|
216
|
-
for auth_contract, auth_method in zip(auth_contracts, wallet.access_list):
|
217
|
-
auth_class = AUTH_CLASS[auth_method.account_id]
|
218
|
-
if not isinstance(auth_contract, auth_class):
|
219
|
-
raise ValueError(
|
220
|
-
f"Auth method {auth_method.account_id} is not supported for this auth class"
|
221
|
-
)
|
222
|
-
user_payloads.append(auth_contract.generate_user_payload(msg_hash))
|
223
|
-
return await self.near_account.function_call(
|
224
|
-
_WALLET_REGISTER,
|
225
|
-
"add_access_rule",
|
226
|
-
{
|
227
|
-
"wallet_id": self.wallet_id,
|
228
|
-
"rule": access.model_dump(),
|
229
|
-
"user_payloads": user_payloads,
|
230
|
-
},
|
231
|
-
included=True,
|
232
|
-
)
|
233
|
-
|
234
|
-
async def remove_access_rule(
|
235
|
-
self, access_id: int, auth_contracts: List[AuthContract]
|
236
|
-
):
|
237
|
-
wallet = await self.get_wallet()
|
238
|
-
message_body = f"REMOVE_AUTH_METHOD:{access_id}:{self.wallet_id}".encode(
|
239
|
-
"utf-8"
|
240
|
-
)
|
241
|
-
msg_hash = sha256(message_body).digest()
|
242
|
-
user_payloads = []
|
243
|
-
if len(auth_contracts) != len(wallet.access_list):
|
244
|
-
raise ValueError("Auth methods count should be equal to wallet access list")
|
245
|
-
for auth_contract, auth_method in zip(auth_contracts, wallet.access_list):
|
246
|
-
auth_class = AUTH_CLASS[auth_method.account_id]
|
247
|
-
if not isinstance(auth_contract, auth_class):
|
248
|
-
raise ValueError(
|
249
|
-
f"Auth method {auth_method.account_id} is not supported for this auth class"
|
250
|
-
)
|
251
|
-
user_payloads.append(auth_contract.generate_user_payload(msg_hash))
|
252
|
-
return await self.near_account.function_call(
|
253
|
-
_WALLET_REGISTER,
|
254
|
-
"remove_access_rule",
|
255
|
-
{
|
256
|
-
"wallet_id": self.wallet_id,
|
257
|
-
"rule_id": access_id,
|
258
|
-
"user_payloads": user_payloads,
|
259
|
-
},
|
260
|
-
included=True,
|
261
|
-
)
|
File without changes
|