Skip to content

Commit

Permalink
Fix: Added EVMVerifier class that is used on all EVM chains. Also add…
Browse files Browse the repository at this point in the history
…ed that chains to the SignatureVerifier as EVM chains.
  • Loading branch information
nesitor committed Oct 31, 2024
1 parent 476c35f commit bf256bb
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 39 deletions.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license = { file = "LICENSE.txt" }
authors = [
{ name = "Moshe Malawach", email = "moshe.malawach@protonmail.com" },
]
requires-python = ">=3.10,<3.13"
requires-python = ">=3.12,<3.13"
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
Expand All @@ -32,7 +32,7 @@ dependencies = [
"aleph-nuls2==0.1",
"aleph-p2p-client @ git+https://github.com/aleph-im/p2p-service-client-python@2c04af39c566217f629fd89505ffc3270fba8676",
"aleph-pytezos==3.13.4",
"asyncpg==0.30.0",
"asyncpg==0.30",
"base58>=1.0.3",
"coincurve==20",
"configmanager==1.35.1",
Expand Down
41 changes: 4 additions & 37 deletions src/aleph/chains/ethereum.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
import functools
import importlib.resources
import json
import logging
Expand All @@ -8,7 +7,6 @@
from aleph_message.models import Chain
from configmanager import Config
from eth_account import Account
from eth_account.messages import encode_defunct
from hexbytes import HexBytes
from web3 import Web3
from web3._utils.events import get_event_data
Expand All @@ -17,21 +15,20 @@
from web3.middleware.filter import local_filter_middleware
from web3.middleware.geth_poa import geth_poa_middleware

from aleph.chains.common import get_verification_buffer
from aleph.db.accessors.chains import get_last_height, upsert_chain_sync_status
from aleph.db.accessors.messages import get_unconfirmed_messages
from aleph.db.accessors.pending_messages import count_pending_messages
from aleph.db.accessors.pending_txs import count_pending_txs
from aleph.db.models.chains import ChainTxDb
from aleph.schemas.chains.tx_context import TxContext
from aleph.schemas.pending_messages import BasePendingMessage
from aleph.toolkit.timestamp import utc_now
from aleph.types.chain_sync import ChainEventType
from aleph.types.db_session import DbSessionFactory
from aleph.utils import run_in_executor

from .abc import ChainWriter, Verifier
from .abc import ChainWriter
from .chain_data_service import ChainDataService, PendingTxPublisher
from .evm import EVMVerifier
from .indexer_reader import AlephIndexerReader

LOGGER = logging.getLogger("chains.ethereum")
Expand Down Expand Up @@ -68,38 +65,8 @@ def get_logs_query(web3: Web3, contract, start_height, end_height):
)


class EthereumVerifier(Verifier):
async def verify_signature(self, message: BasePendingMessage) -> bool:
"""Verifies a signature of a message, return True if verified, false if not"""

verification = get_verification_buffer(message)

message_hash = await run_in_executor(
None, functools.partial(encode_defunct, text=verification.decode("utf-8"))
)

verified = False
try:
# we assume the signature is a valid string
address = await run_in_executor(
None,
functools.partial(
Account.recover_message, message_hash, signature=message.signature
),
)
if address == message.sender:
verified = True
else:
LOGGER.warning(
"Received bad signature from %s for %s" % (address, message.sender)
)
return False

except Exception:
LOGGER.exception("Error processing signature for %s" % message.sender)
verified = False

return verified
class EthereumVerifier(EVMVerifier):
pass


class EthereumConnector(ChainWriter):
Expand Down
47 changes: 47 additions & 0 deletions src/aleph/chains/evm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import functools
import logging

from eth_account import Account
from eth_account.messages import encode_defunct

from aleph.chains.common import get_verification_buffer
from aleph.schemas.pending_messages import BasePendingMessage
from aleph.utils import run_in_executor

from .abc import Verifier

LOGGER = logging.getLogger("chains.evm")


class EVMVerifier(Verifier):
async def verify_signature(self, message: BasePendingMessage) -> bool:
"""Verifies a signature of a message, return True if verified, false if not"""

verification = get_verification_buffer(message)

message_hash = await run_in_executor(
None, functools.partial(encode_defunct, text=verification.decode("utf-8"))
)

verified = False
try:
# we assume the signature is a valid string
address = await run_in_executor(
None,
functools.partial(
Account.recover_message, message_hash, signature=message.signature
),
)
if address == message.sender:
verified = True
else:
LOGGER.warning(
"Received bad signature from %s for %s" % (address, message.sender)
)
return False

except Exception:
LOGGER.exception("Error processing signature for %s" % message.sender)
verified = False

return verified
15 changes: 15 additions & 0 deletions src/aleph/chains/signature_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from aleph.chains.abc import Verifier
from aleph.chains.avalanche import AvalancheConnector
from aleph.chains.ethereum import EthereumVerifier
from aleph.chains.evm import EVMVerifier
from aleph.chains.nuls import NulsConnector
from aleph.chains.nuls2 import Nuls2Verifier
from aleph.chains.solana import SolanaConnector
Expand All @@ -19,13 +20,27 @@ class SignatureVerifier:

def __init__(self):
self.verifiers = {
Chain.ARBITRUM: EVMVerifier(),
Chain.AVAX: AvalancheConnector(),
Chain.BLAST: EVMVerifier(),
Chain.BOB: EVMVerifier(),
Chain.CYBER: EVMVerifier(),
Chain.DOT: SubstrateConnector(),
Chain.ETH: EthereumVerifier(),
Chain.FRAXTAL: EVMVerifier(),
Chain.INK: EVMVerifier(),
Chain.METIS: EVMVerifier(),
Chain.MODE: EVMVerifier(),
Chain.NULS: NulsConnector(),
Chain.NULS2: Nuls2Verifier(),
Chain.LINEA: EVMVerifier(),
Chain.LISK: EVMVerifier(),
Chain.OPTIMISM: EVMVerifier(),
Chain.POL: EVMVerifier(),
Chain.SOL: SolanaConnector(),
Chain.TEZOS: TezosVerifier(),
Chain.WORLDCHAIN: EVMVerifier(),
Chain.ZORA: EVMVerifier(),
}

async def verify_signature(self, message: BasePendingMessage) -> None:
Expand Down
36 changes: 36 additions & 0 deletions tests/chains/test_evm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

from aleph.chains.evm import EVMVerifier
from aleph.schemas.pending_messages import BasePendingMessage, parse_message


@pytest.fixture
def evm_message() -> BasePendingMessage:
return parse_message(
{
"item_hash": "3d1909797f30fa7868d9cc1138128687d1b501de1ab8bbde40caa4e5a03e02bc",
"type": "POST",
"chain": "SOL",
"sender": "0xA07B1214bAe0D5ccAA25449C3149c0aC83658874",
"signature": "0x006f3014418130fc89fb910ebc0a7742ca7575e72487418482a6990a48250977236674cb603fc5bc0caf5f0ce2ddf3680e9b1947ca3d7698cd2b9af3332135191c",
"item_type": "inline",
"item_content": '{"type":"polygon","address":"0xA07B1214bAe0D5ccAA25449C3149c0aC83658874","content":{"body":"This message was posted from the typescript-SDK test suite"},"time":1689163528.372}',
"time": 1689163528.372,
"channel": "TEST",
}
)


@pytest.mark.asyncio
async def test_verify_evm_signature_real(evm_message: BasePendingMessage):
verifier = EVMVerifier()
result = await verifier.verify_signature(evm_message)
assert result is True


@pytest.mark.asyncio
async def test_verify_bad_evm_signature(evm_message: BasePendingMessage):
verifier = EVMVerifier()
evm_message.signature = "baba"
result = await verifier.verify_signature(evm_message)
assert result is False

0 comments on commit bf256bb

Please sign in to comment.