Skip to content

Commit e514236

Browse files
committed
fix COSMOS
1 parent 5fd68b4 commit e514236

File tree

4 files changed

+135
-8
lines changed

4 files changed

+135
-8
lines changed

src/aleph/sdk/account.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from aleph_message.models import Chain
77

88
from aleph.sdk.chains.common import get_fallback_private_key
9+
from aleph.sdk.chains.cosmos import CSDKAccount
910
from aleph.sdk.chains.ethereum import ETHAccount
1011
from aleph.sdk.chains.evm import EVMAccount
1112
from aleph.sdk.chains.remote import RemoteAccount
@@ -38,6 +39,7 @@
3839
Chain.SOL: SOLAccount,
3940
Chain.WORLDCHAIN: EVMAccount,
4041
Chain.ZORA: EVMAccount,
42+
Chain.CSDK: CSDKAccount,
4143
}
4244

4345

src/aleph/sdk/chains/cosmos.py

+34-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import base64
22
import hashlib
33
import json
4-
from typing import Union
4+
from pathlib import Path
5+
from typing import Optional, Union
56

67
import ecdsa
78
from cosmospy._wallet import privkey_to_address, privkey_to_pubkey
9+
from ecdsa import BadSignatureError
810

911
from .common import BaseAccount, get_fallback_private_key, get_verification_buffer
1012

@@ -52,7 +54,7 @@ def __init__(self, private_key=None, hrp=DEFAULT_HRP):
5254
async def sign_message(self, message):
5355
message = self._setup_sender(message)
5456
verif = get_verification_string(message)
55-
base64_pubkey = base64.b64encode(self.get_public_key().encode()).decode("utf-8")
57+
base64_pubkey = base64.b64encode(self.get_public_key()).decode("utf-8")
5658
signature = await self.sign_raw(verif.encode("utf-8"))
5759

5860
sig = {
@@ -78,17 +80,41 @@ def get_address(self) -> str:
7880
return privkey_to_address(self.private_key)
7981

8082
def get_public_key(self) -> str:
81-
return privkey_to_pubkey(self.private_key).decode()
83+
return privkey_to_pubkey(self.private_key)
8284

8385

84-
def get_fallback_account(hrp=DEFAULT_HRP):
85-
return CSDKAccount(private_key=get_fallback_private_key(), hrp=hrp)
86+
def get_fallback_account(path: Optional[Path] = None, hrp=DEFAULT_HRP):
87+
return CSDKAccount(private_key=get_fallback_private_key(path=path), hrp=hrp)
8688

8789

8890
def verify_signature(
8991
signature: Union[bytes, str],
9092
public_key: Union[bytes, str],
9193
message: Union[bytes, str],
92-
) -> bool:
93-
"""TODO: Implement this"""
94-
raise NotImplementedError("Not implemented yet")
94+
):
95+
"""
96+
Verifies a signature.
97+
Args:
98+
signature: The signature to verify. Can be a base64 encoded string or bytes.
99+
public_key: The public key to use for verification. Can be a base64 encoded string or bytes.
100+
message: The message to verify. Can be an utf-8 string or bytes.
101+
Raises:
102+
BadSignatureError: If the signature is invalid.!
103+
"""
104+
105+
if isinstance(signature, str):
106+
signature = base64.b64decode(signature.encode("utf-8"))
107+
if isinstance(public_key, str):
108+
public_key = base64.b64decode(public_key)
109+
if isinstance(message, str):
110+
message = message.encode("utf-8")
111+
112+
vk = ecdsa.VerifyingKey.from_string(public_key, curve=ecdsa.SECP256k1)
113+
114+
try:
115+
vk.verify(
116+
signature, message, hashfunc=hashlib.sha256,
117+
)
118+
return True
119+
except Exception as e:
120+
raise BadSignatureError from e

tests/unit/conftest.py

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from aiohttp import ClientResponseError
1212
from aleph_message.models import AggregateMessage, AlephMessage, PostMessage
1313

14+
import aleph.sdk.chains.cosmos as cosmos
1415
import aleph.sdk.chains.ethereum as ethereum
1516
import aleph.sdk.chains.solana as solana
1617
import aleph.sdk.chains.substrate as substrate
@@ -54,6 +55,13 @@ def substrate_account() -> substrate.DOTAccount:
5455
yield substrate.get_fallback_account(path=Path(private_key_file.name))
5556

5657

58+
@pytest.fixture
59+
def cosmos_account() -> cosmos.CSDKAccount:
60+
with NamedTemporaryFile(delete=False) as private_key_file:
61+
private_key_file.close()
62+
yield cosmos.get_fallback_account(path=Path(private_key_file.name))
63+
64+
5765
@pytest.fixture
5866
def json_messages():
5967
messages_path = Path(__file__).parent / "messages.json"

tests/unit/test_chain_cosmos.py

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import base64
2+
import json
3+
from dataclasses import asdict, dataclass
4+
5+
import pytest
6+
from ecdsa import BadSignatureError
7+
8+
from aleph.sdk.chains.common import get_verification_buffer
9+
from aleph.sdk.chains.cosmos import get_verification_string, verify_signature
10+
11+
12+
@dataclass
13+
class Message:
14+
chain: str
15+
sender: str
16+
type: str
17+
item_hash: str
18+
19+
20+
@pytest.mark.asyncio
21+
async def test_verify_signature(cosmos_account):
22+
message = asdict(
23+
Message(
24+
"CSDK",
25+
cosmos_account.get_address(),
26+
"POST",
27+
"SomeHash",
28+
)
29+
)
30+
await cosmos_account.sign_message(message)
31+
assert message["signature"]
32+
signature = json.loads(message["signature"])
33+
raw_signature = signature["signature"]
34+
assert isinstance(raw_signature, str)
35+
36+
pub_key = base64.b64decode(signature["pub_key"]["value"])
37+
38+
verify_signature(
39+
raw_signature,
40+
pub_key,
41+
get_verification_string(message),
42+
)
43+
44+
45+
@pytest.mark.asyncio
46+
async def test_verify_signature_raw(cosmos_account):
47+
message = asdict(
48+
Message(
49+
"CSDK",
50+
cosmos_account.get_address(),
51+
"POST",
52+
"SomeHash",
53+
)
54+
)
55+
await cosmos_account.sign_message(message)
56+
raw_message = get_verification_buffer(message)
57+
raw_signature = await cosmos_account.sign_raw(raw_message)
58+
assert isinstance(raw_signature, bytes)
59+
60+
pub_key = cosmos_account.get_public_key()
61+
verify_signature(
62+
raw_signature.decode(),
63+
pub_key,
64+
raw_message,
65+
)
66+
67+
68+
@pytest.mark.asyncio
69+
async def test_bad_signature(cosmos_account):
70+
message = asdict(
71+
Message(
72+
"CSDK",
73+
cosmos_account.get_address(),
74+
"POST",
75+
"SomeHash",
76+
)
77+
)
78+
await cosmos_account.sign_message(message)
79+
assert message["signature"]
80+
signature = json.loads(message["signature"])
81+
raw_signature = "1" + signature["signature"]
82+
assert isinstance(raw_signature, str)
83+
84+
pub_key = base64.b64decode(signature["pub_key"]["value"])
85+
86+
with pytest.raises(BadSignatureError):
87+
verify_signature(
88+
raw_signature,
89+
pub_key,
90+
get_verification_string(message),
91+
)

0 commit comments

Comments
 (0)