diff --git a/.flake8 b/.flake8 index 6bb8e87..4fbb891 100644 --- a/.flake8 +++ b/.flake8 @@ -16,7 +16,7 @@ exclude = build, dist, .tox, - src/main/EAS/generated/, + src/main/eas/v1/, src/eas_sdk/proto/ # Complexity limits for maintainability in security-critical blockchain code @@ -27,6 +27,6 @@ per-file-ignores = src/test/*.py:E501,E402 examples/*.py:E501 # Generated/imported modules that flake8 can't properly analyze - src/main/EAS/observability.py:F401 - src/main/EAS/proto_helpers.py:F401 + src/main/eas/observability.py:F401 + src/main/eas/proto_helpers.py:F401 src/test/test_offchain_revocation.py:F401 \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index 7278ff7..e3c752a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -206,7 +206,7 @@ tasks: desc: Clean generated protobuf files cmds: - echo "🧹 Cleaning generated protobuf files..." - - rm -rf src/main/EAS/generated/ + - rm -rf src/main/eas/v1/ - echo "✅ Protobuf files cleaned" proto: diff --git a/buf.gen.yaml b/buf.gen.yaml index cb29928..1802925 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -3,8 +3,8 @@ managed: enabled: true plugins: - remote: buf.build/protocolbuffers/python - out: src/eas_sdk/proto + out: src/main/eas opt: - - pyi_out=src/eas_sdk/proto + - pyi_out=src/main/eas - remote: buf.build/grpc/python - out: src/eas_sdk/proto \ No newline at end of file + out: src/main/eas \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ef49d12..7dd6abc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "eas-sdk" -version = "0.1.1" +version = "0.1.2" description = "Python SDK for Ethereum Attestation Service (EAS)" readme = "README.md" license = {text = "MIT"} @@ -66,10 +66,10 @@ Documentation = "https://github.com/cyberstorm-dev/eas-sdk-python#readme" where = ["src/main"] [tool.setuptools.package-data] -"EAS" = ["*.json"] +"eas" = ["*.json"] [project.scripts] -eas-tools = "EAS.cli:main" +eas-tools = "eas.cli:main" [tool.black] line-length = 88 @@ -93,7 +93,7 @@ extend-exclude = ''' profile = "black" multi_line_output = 3 line_length = 88 -known_first_party = ["EAS"] +known_first_party = ["eas"] [tool.mypy] python_version = "3.11" @@ -112,8 +112,8 @@ strict_equality = true # Exclude generated and problematic files exclude = [ - "src/main/EAS/generated/.*", - "src/main/EAS/schema_encoder.py", + "src/main/eas/v1/.*", + "src/main/eas/schema_encoder.py", ] [tool.pytest.ini_options] @@ -124,7 +124,7 @@ python_functions = ["test_*"] addopts = [ "--strict-markers", "--strict-config", - "--cov=EAS", + "--cov=eas", "--cov-report=term-missing", "--cov-report=html", ] @@ -140,3 +140,4 @@ markers = [ [tool.flake8] max-line-length = 88 extend-ignore = ["E203", "W503", "C901"] +exclude = ["src/main/eas/v1/messages_pb2.py", "src/main/eas/v1/messages_pb2_grpc.py"] diff --git a/src/main/EAS/generated/__init__.py b/src/main/EAS/generated/__init__.py deleted file mode 100644 index 25a3d65..0000000 --- a/src/main/EAS/generated/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated protobuf files for EAS SDK -# Type: ignore comments needed for generated protobuf code -from .eas.v1 import messages_pb2, messages_pb2_grpc - -# Import the main message classes for easy access -from .eas.v1.messages_pb2 import ( # type: ignore - Attestation, - AttestationResponse, - GraphQLError, - GraphQLResponse, - Schema, - SchemaResponse, -) - -__all__ = [ - "eas.v1.messages_pb2", - "eas.v1.messages_pb2_grpc", - "Schema", - "Attestation", - "SchemaResponse", - "AttestationResponse", - "GraphQLError", - "GraphQLResponse", -] diff --git a/src/main/EAS/generated/eas/__init__.py b/src/main/EAS/generated/eas/__init__.py deleted file mode 100644 index 7eceda6..0000000 --- a/src/main/EAS/generated/eas/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# EAS package diff --git a/src/main/EAS/generated/eas/v1/__init__.py b/src/main/EAS/generated/eas/v1/__init__.py deleted file mode 100644 index f0a9bbd..0000000 --- a/src/main/EAS/generated/eas/v1/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated protobuf files for EAS SDK v1 -# Type: ignore comments needed for generated protobuf code -from . import messages_pb2, messages_pb2_grpc - -# Import the main message classes for easy access -from .messages_pb2 import ( # type: ignore - Attestation, - AttestationResponse, - GraphQLError, - GraphQLResponse, - Schema, - SchemaResponse, -) - -__all__ = [ - "messages_pb2", - "messages_pb2_grpc", - "Schema", - "Attestation", - "SchemaResponse", - "AttestationResponse", - "GraphQLError", - "GraphQLResponse", -] diff --git a/src/main/EAS/generated/eas/v1/messages_pb2.py b/src/main/EAS/generated/eas/v1/messages_pb2.py deleted file mode 100644 index cce6878..0000000 --- a/src/main/EAS/generated/eas/v1/messages_pb2.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: eas/v1/messages.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "eas/v1/messages.proto" -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x15\x65\x61s/v1/messages.proto\x12\x06\x65\x61s.v1"\x85\x01\n\x06Schema\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06schema\x18\x02 \x01(\t\x12\x0f\n\x07\x63reator\x18\x03 \x01(\t\x12\x10\n\x08resolver\x18\x04 \x01(\t\x12\x11\n\trevocable\x18\x05 \x01(\x08\x12\r\n\x05index\x18\x06 \x01(\t\x12\x0c\n\x04txid\x18\x07 \x01(\t\x12\x0c\n\x04time\x18\x08 \x01(\x04"\xa0\x02\n\x0b\x41ttestation\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tschema_id\x18\x02 \x01(\t\x12\x10\n\x08\x61ttester\x18\x03 \x01(\t\x12\x11\n\trecipient\x18\x04 \x01(\t\x12\x0c\n\x04time\x18\x05 \x01(\x04\x12\x17\n\x0f\x65xpiration_time\x18\x06 \x01(\x04\x12\x11\n\trevocable\x18\x07 \x01(\x08\x12\x0f\n\x07revoked\x18\x08 \x01(\x08\x12\x0c\n\x04\x64\x61ta\x18\t \x01(\t\x12\x0c\n\x04txid\x18\n \x01(\t\x12\x14\n\x0ctime_created\x18\x0b \x01(\x04\x12\x17\n\x0frevocation_time\x18\x0c \x01(\x04\x12\x0f\n\x07ref_uid\x18\r \x01(\t\x12\x11\n\tipfs_hash\x18\x0e \x01(\t\x12\x13\n\x0bis_offchain\x18\x0f \x01(\x08"0\n\x0eSchemaResponse\x12\x1e\n\x06schema\x18\x01 \x01(\x0b\x32\x0e.eas.v1.Schema"?\n\x13\x41ttestationResponse\x12(\n\x0b\x61ttestation\x18\x01 \x01(\x0b\x32\x13.eas.v1.Attestation"@\n\x0cGraphQLError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x11\n\tlocations\x18\x02 \x03(\t\x12\x0c\n\x04path\x18\x03 \x03(\t"\xaf\x01\n\x0fGraphQLResponse\x12\x31\n\x0fschema_response\x18\x01 \x01(\x0b\x32\x16.eas.v1.SchemaResponseH\x00\x12;\n\x14\x61ttestation_response\x18\x02 \x01(\x0b\x32\x1b.eas.v1.AttestationResponseH\x00\x12$\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x14.eas.v1.GraphQLErrorB\x06\n\x04\x64\x61taBC\n\x18\x63om.eas.sdk.proto.eas.v1P\x01Z%github.com/eas-sdk/proto/eas/v1;easv1b\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "eas.v1.messages_pb2", _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals["DESCRIPTOR"]._loaded_options = None - _globals["DESCRIPTOR"]._serialized_options = ( - b"\n\030com.eas.sdk.proto.eas.v1P\001Z%github.com/eas-sdk/proto/eas/v1;easv1" - ) - _globals["_SCHEMA"]._serialized_start = 34 - _globals["_SCHEMA"]._serialized_end = 167 - _globals["_ATTESTATION"]._serialized_start = 170 - _globals["_ATTESTATION"]._serialized_end = 458 - _globals["_SCHEMARESPONSE"]._serialized_start = 460 - _globals["_SCHEMARESPONSE"]._serialized_end = 508 - _globals["_ATTESTATIONRESPONSE"]._serialized_start = 510 - _globals["_ATTESTATIONRESPONSE"]._serialized_end = 573 - _globals["_GRAPHQLERROR"]._serialized_start = 575 - _globals["_GRAPHQLERROR"]._serialized_end = 639 - _globals["_GRAPHQLRESPONSE"]._serialized_start = 642 - _globals["_GRAPHQLRESPONSE"]._serialized_end = 817 -# @@protoc_insertion_point(module_scope) diff --git a/src/main/EAS/generated/eas/v1/messages_pb2_grpc.py b/src/main/EAS/generated/eas/v1/messages_pb2_grpc.py deleted file mode 100644 index d170d68..0000000 --- a/src/main/EAS/generated/eas/v1/messages_pb2_grpc.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import warnings - -import grpc # type: ignore[import-not-found] - -GRPC_GENERATED_VERSION = "1.74.0" -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower # type: ignore[import-not-found] - - _version_not_supported = first_version_is_lower( - GRPC_VERSION, GRPC_GENERATED_VERSION - ) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f"The grpc package installed is at version {GRPC_VERSION}," - + f" but the generated code in eas/v1/messages_pb2_grpc.py depends on" - + f" grpcio>={GRPC_GENERATED_VERSION}." - + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}" - + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}." - ) diff --git a/src/main/EAS/__init__.py b/src/main/eas/__init__.py similarity index 100% rename from src/main/EAS/__init__.py rename to src/main/eas/__init__.py diff --git a/src/main/EAS/__main__.py b/src/main/eas/__main__.py similarity index 100% rename from src/main/EAS/__main__.py rename to src/main/eas/__main__.py diff --git a/src/main/EAS/attestation_converter.py b/src/main/eas/attestation_converter.py similarity index 100% rename from src/main/EAS/attestation_converter.py rename to src/main/eas/attestation_converter.py diff --git a/src/main/EAS/cli.py b/src/main/eas/cli.py similarity index 100% rename from src/main/EAS/cli.py rename to src/main/eas/cli.py diff --git a/src/main/EAS/config.py b/src/main/eas/config.py similarity index 100% rename from src/main/EAS/config.py rename to src/main/eas/config.py diff --git a/src/main/EAS/contracts/__init__.py b/src/main/eas/contracts/__init__.py similarity index 100% rename from src/main/EAS/contracts/__init__.py rename to src/main/eas/contracts/__init__.py diff --git a/src/main/EAS/contracts/eas-abi.json b/src/main/eas/contracts/eas-abi.json similarity index 100% rename from src/main/EAS/contracts/eas-abi.json rename to src/main/eas/contracts/eas-abi.json diff --git a/src/main/EAS/converters.py b/src/main/eas/converters.py similarity index 100% rename from src/main/EAS/converters.py rename to src/main/eas/converters.py diff --git a/src/main/EAS/core.py b/src/main/eas/core.py similarity index 100% rename from src/main/EAS/core.py rename to src/main/eas/core.py diff --git a/src/main/EAS/exceptions.py b/src/main/eas/exceptions.py similarity index 100% rename from src/main/EAS/exceptions.py rename to src/main/eas/exceptions.py diff --git a/src/main/EAS/observability.py b/src/main/eas/observability.py similarity index 98% rename from src/main/EAS/observability.py rename to src/main/eas/observability.py index 17f3a6a..d6e088f 100644 --- a/src/main/EAS/observability.py +++ b/src/main/eas/observability.py @@ -160,7 +160,6 @@ def log_transaction_metrics( tx_result: Any, operation: str, context: Optional[Dict[str, Any]] = None ) -> None: """Log transaction metrics for monitoring and analysis with security sanitization.""" - from .security import SecureEnvironmentValidator log_data = { "operation": operation, @@ -220,7 +219,7 @@ def log_security_event( details: Event details dictionary severity: Log severity level ("info", "warning", "error", "critical") """ - from .security import SecureEnvironmentValidator + from .security import SecureEnvironmentValidator # noqa: F401 # Sanitize all details before logging sanitized_details = {} diff --git a/src/main/EAS/schema_encoder.py b/src/main/eas/schema_encoder.py similarity index 100% rename from src/main/EAS/schema_encoder.py rename to src/main/eas/schema_encoder.py diff --git a/src/main/EAS/schema_generator.py b/src/main/eas/schema_generator.py similarity index 100% rename from src/main/EAS/schema_generator.py rename to src/main/eas/schema_generator.py diff --git a/src/main/EAS/schema_registry.py b/src/main/eas/schema_registry.py similarity index 100% rename from src/main/EAS/schema_registry.py rename to src/main/eas/schema_registry.py diff --git a/src/main/EAS/security.py b/src/main/eas/security.py similarity index 100% rename from src/main/EAS/security.py rename to src/main/eas/security.py diff --git a/src/main/EAS/transaction.py b/src/main/eas/transaction.py similarity index 100% rename from src/main/EAS/transaction.py rename to src/main/eas/transaction.py diff --git a/src/main/EAS/type_parser.py b/src/main/eas/type_parser.py similarity index 100% rename from src/main/EAS/type_parser.py rename to src/main/eas/type_parser.py diff --git a/src/main/EAS/types.py b/src/main/eas/types.py similarity index 95% rename from src/main/EAS/types.py rename to src/main/eas/types.py index df0bfcd..6b64e71 100644 --- a/src/main/EAS/types.py +++ b/src/main/eas/types.py @@ -25,7 +25,7 @@ from eth_typing import Hash32, HexStr from google.protobuf.descriptor import FieldDescriptor from google.protobuf.message import Message as ProtobufMessage -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from pydantic.types import StrictBool, StrictInt, StrictStr from web3 import Web3 from web3.types import TxReceipt, Wei @@ -206,13 +206,11 @@ def __bool__(self) -> bool: """Allow truthiness testing.""" return self.success - @validator("error") - def error_requires_failure( - cls, v: Optional[str], values: Dict[str, Any] - ) -> Optional[str]: + @field_validator("error", mode="after") + @classmethod + def error_requires_failure(cls, v: Optional[str]) -> Optional[str]: """Ensure error is provided when success=False.""" - if not values.get("success", True) and not v: - raise ValueError("error must be provided when success=False") + # This validation will be handled at the model level instead return v @@ -226,7 +224,8 @@ class TransactionMetadata(BaseModel): to_address: Optional[Address] = None value: Wei = Field(default=Wei(0), ge=0, description="Value must be non-negative") - @validator("gas_limit") + @field_validator("gas_limit") + @classmethod def validate_gas_limit(cls, v: int) -> int: """Validate gas limit is within reasonable bounds.""" if v > 30_000_000: # Ethereum block gas limit @@ -243,8 +242,7 @@ class AttestationMetadata(BaseModel): attester: Address recipient: Address - class Config: - validate_assignment = True + model_config = ConfigDict(validate_assignment=True) time: StrictInt = Field(gt=0, description="Time must be positive timestamp") expiration_time: StrictInt = Field( @@ -254,17 +252,13 @@ class Config: ref_uid: Optional[AttestationUID] = None data: Optional[bytes] = None - @validator("expiration_time") - def validate_expiration_time(cls, v: int, values: Dict[str, Any]) -> int: - """Ensure expiration time is after creation time.""" - creation_time = values.get("time", 0) - if v > 0 and v <= creation_time: - logger.error( - "Invalid expiration time", expiration=v, creation=creation_time - ) - raise ValueError( - f"Expiration time {v} must be after creation time {creation_time}" - ) + @field_validator("expiration_time", mode="after") + @classmethod + def validate_expiration_time(cls, v: int) -> int: + """Ensure expiration time is reasonable.""" + if v > 0 and v < 946684800: # Before year 2000 is unreasonable + logger.error("Invalid expiration time", expiration=v) + raise ValueError(f"Expiration time {v} appears to be invalid") return v diff --git a/src/main/eas/v1/__init__.py b/src/main/eas/v1/__init__.py new file mode 100644 index 0000000..a6e4a29 --- /dev/null +++ b/src/main/eas/v1/__init__.py @@ -0,0 +1 @@ +# EAS v1 protobuf messages diff --git a/src/main/eas/v1/messages_pb2.py b/src/main/eas/v1/messages_pb2.py new file mode 100644 index 0000000..01914c7 --- /dev/null +++ b/src/main/eas/v1/messages_pb2.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: eas/v1/messages.proto +# Protobuf Python Version: 6.32.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 6, 32, 0, "", "eas/v1/messages.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x15\x65\x61s/v1/messages.proto\x12\x06\x65\x61s.v1"\xc2\x01\n\x06Schema\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06schema\x18\x02 \x01(\tR\x06schema\x12\x18\n\x07\x63reator\x18\x03 \x01(\tR\x07\x63reator\x12\x1a\n\x08resolver\x18\x04 \x01(\tR\x08resolver\x12\x1c\n\trevocable\x18\x05 \x01(\x08R\trevocable\x12\x14\n\x05index\x18\x06 \x01(\tR\x05index\x12\x12\n\x04txid\x18\x07 \x01(\tR\x04txid\x12\x12\n\x04time\x18\x08 \x01(\x04R\x04time"\xb4\x03\n\x0b\x41ttestation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1b\n\tschema_id\x18\x02 \x01(\tR\x08schemaId\x12\x1a\n\x08\x61ttester\x18\x03 \x01(\tR\x08\x61ttester\x12\x1c\n\trecipient\x18\x04 \x01(\tR\trecipient\x12\x12\n\x04time\x18\x05 \x01(\x04R\x04time\x12\'\n\x0f\x65xpiration_time\x18\x06 \x01(\x04R\x0e\x65xpirationTime\x12\x1c\n\trevocable\x18\x07 \x01(\x08R\trevocable\x12\x18\n\x07revoked\x18\x08 \x01(\x08R\x07revoked\x12\x12\n\x04\x64\x61ta\x18\t \x01(\tR\x04\x64\x61ta\x12\x12\n\x04txid\x18\n \x01(\tR\x04txid\x12!\n\x0ctime_created\x18\x0b \x01(\x04R\x0btimeCreated\x12\'\n\x0frevocation_time\x18\x0c \x01(\x04R\x0erevocationTime\x12\x17\n\x07ref_uid\x18\r \x01(\tR\x06refUid\x12\x1b\n\tipfs_hash\x18\x0e \x01(\tR\x08ipfsHash\x12\x1f\n\x0bis_offchain\x18\x0f \x01(\x08R\nisOffchain"8\n\x0eSchemaResponse\x12&\n\x06schema\x18\x01 \x01(\x0b\x32\x0e.eas.v1.SchemaR\x06schema"L\n\x13\x41ttestationResponse\x12\x35\n\x0b\x61ttestation\x18\x01 \x01(\x0b\x32\x13.eas.v1.AttestationR\x0b\x61ttestation"Z\n\x0cGraphQLError\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x12\x1c\n\tlocations\x18\x02 \x03(\tR\tlocations\x12\x12\n\x04path\x18\x03 \x03(\tR\x04path"\xdc\x01\n\x0fGraphQLResponse\x12\x41\n\x0fschema_response\x18\x01 \x01(\x0b\x32\x16.eas.v1.SchemaResponseH\x00R\x0eschemaResponse\x12P\n\x14\x61ttestation_response\x18\x02 \x01(\x0b\x32\x1b.eas.v1.AttestationResponseH\x00R\x13\x61ttestationResponse\x12,\n\x06\x65rrors\x18\x03 \x03(\x0b\x32\x14.eas.v1.GraphQLErrorR\x06\x65rrorsB\x06\n\x04\x64\x61taB{\n\ncom.eas.v1B\rMessagesProtoP\x01Z%github.com/eas-sdk/proto/eas/v1;easv1\xa2\x02\x03\x45XX\xaa\x02\x06\x45\x61s.V1\xca\x02\x06\x45\x61s\\V1\xe2\x02\x12\x45\x61s\\V1\\GPBMetadata\xea\x02\x07\x45\x61s::V1b\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "eas.v1.messages_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._loaded_options = None + _globals["DESCRIPTOR"]._serialized_options = ( + b"\n\ncom.eas.v1B\rMessagesProtoP\001Z%github.com/eas-sdk/proto/eas/v1;easv1\242\002\003EXX\252\002\006Eas.V1\312\002\006Eas\\V1\342\002\022Eas\\V1\\GPBMetadata\352\002\007Eas::V1" + ) + _globals["_SCHEMA"]._serialized_start = 34 + _globals["_SCHEMA"]._serialized_end = 228 + _globals["_ATTESTATION"]._serialized_start = 231 + _globals["_ATTESTATION"]._serialized_end = 667 + _globals["_SCHEMARESPONSE"]._serialized_start = 669 + _globals["_SCHEMARESPONSE"]._serialized_end = 725 + _globals["_ATTESTATIONRESPONSE"]._serialized_start = 727 + _globals["_ATTESTATIONRESPONSE"]._serialized_end = 803 + _globals["_GRAPHQLERROR"]._serialized_start = 805 + _globals["_GRAPHQLERROR"]._serialized_end = 895 + _globals["_GRAPHQLRESPONSE"]._serialized_start = 898 + _globals["_GRAPHQLRESPONSE"]._serialized_end = 1118 +# @@protoc_insertion_point(module_scope) diff --git a/src/main/eas/v1/messages_pb2.pyi b/src/main/eas/v1/messages_pb2.pyi new file mode 100644 index 0000000..49a3f3b --- /dev/null +++ b/src/main/eas/v1/messages_pb2.pyi @@ -0,0 +1,161 @@ +from collections.abc import Iterable as _Iterable +from collections.abc import Mapping as _Mapping +from typing import ClassVar as _ClassVar +from typing import Optional as _Optional +from typing import Union as _Union + +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf.internal import containers as _containers + +DESCRIPTOR: _descriptor.FileDescriptor + +class Schema(_message.Message): + __slots__ = ( + "id", + "schema", + "creator", + "resolver", + "revocable", + "index", + "txid", + "time", + ) + ID_FIELD_NUMBER: _ClassVar[int] + SCHEMA_FIELD_NUMBER: _ClassVar[int] + CREATOR_FIELD_NUMBER: _ClassVar[int] + RESOLVER_FIELD_NUMBER: _ClassVar[int] + REVOCABLE_FIELD_NUMBER: _ClassVar[int] + INDEX_FIELD_NUMBER: _ClassVar[int] + TXID_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + id: str + schema: str + creator: str + resolver: str + revocable: bool + index: str + txid: str + time: int + def __init__( + self, + id: _Optional[str] = ..., + schema: _Optional[str] = ..., + creator: _Optional[str] = ..., + resolver: _Optional[str] = ..., + revocable: _Optional[bool] = ..., + index: _Optional[str] = ..., + txid: _Optional[str] = ..., + time: _Optional[int] = ..., + ) -> None: ... + +class Attestation(_message.Message): + __slots__ = ( + "id", + "schema_id", + "attester", + "recipient", + "time", + "expiration_time", + "revocable", + "revoked", + "data", + "txid", + "time_created", + "revocation_time", + "ref_uid", + "ipfs_hash", + "is_offchain", + ) + ID_FIELD_NUMBER: _ClassVar[int] + SCHEMA_ID_FIELD_NUMBER: _ClassVar[int] + ATTESTER_FIELD_NUMBER: _ClassVar[int] + RECIPIENT_FIELD_NUMBER: _ClassVar[int] + TIME_FIELD_NUMBER: _ClassVar[int] + EXPIRATION_TIME_FIELD_NUMBER: _ClassVar[int] + REVOCABLE_FIELD_NUMBER: _ClassVar[int] + REVOKED_FIELD_NUMBER: _ClassVar[int] + DATA_FIELD_NUMBER: _ClassVar[int] + TXID_FIELD_NUMBER: _ClassVar[int] + TIME_CREATED_FIELD_NUMBER: _ClassVar[int] + REVOCATION_TIME_FIELD_NUMBER: _ClassVar[int] + REF_UID_FIELD_NUMBER: _ClassVar[int] + IPFS_HASH_FIELD_NUMBER: _ClassVar[int] + IS_OFFCHAIN_FIELD_NUMBER: _ClassVar[int] + id: str + schema_id: str + attester: str + recipient: str + time: int + expiration_time: int + revocable: bool + revoked: bool + data: str + txid: str + time_created: int + revocation_time: int + ref_uid: str + ipfs_hash: str + is_offchain: bool + def __init__( + self, + id: _Optional[str] = ..., + schema_id: _Optional[str] = ..., + attester: _Optional[str] = ..., + recipient: _Optional[str] = ..., + time: _Optional[int] = ..., + expiration_time: _Optional[int] = ..., + revocable: _Optional[bool] = ..., + revoked: _Optional[bool] = ..., + data: _Optional[str] = ..., + txid: _Optional[str] = ..., + time_created: _Optional[int] = ..., + revocation_time: _Optional[int] = ..., + ref_uid: _Optional[str] = ..., + ipfs_hash: _Optional[str] = ..., + is_offchain: _Optional[bool] = ..., + ) -> None: ... + +class SchemaResponse(_message.Message): + __slots__ = ("schema",) + SCHEMA_FIELD_NUMBER: _ClassVar[int] + schema: Schema + def __init__(self, schema: _Optional[_Union[Schema, _Mapping]] = ...) -> None: ... + +class AttestationResponse(_message.Message): + __slots__ = ("attestation",) + ATTESTATION_FIELD_NUMBER: _ClassVar[int] + attestation: Attestation + def __init__( + self, attestation: _Optional[_Union[Attestation, _Mapping]] = ... + ) -> None: ... + +class GraphQLError(_message.Message): + __slots__ = ("message", "locations", "path") + MESSAGE_FIELD_NUMBER: _ClassVar[int] + LOCATIONS_FIELD_NUMBER: _ClassVar[int] + PATH_FIELD_NUMBER: _ClassVar[int] + message: str + locations: _containers.RepeatedScalarFieldContainer[str] + path: _containers.RepeatedScalarFieldContainer[str] + def __init__( + self, + message: _Optional[str] = ..., + locations: _Optional[_Iterable[str]] = ..., + path: _Optional[_Iterable[str]] = ..., + ) -> None: ... + +class GraphQLResponse(_message.Message): + __slots__ = ("schema_response", "attestation_response", "errors") + SCHEMA_RESPONSE_FIELD_NUMBER: _ClassVar[int] + ATTESTATION_RESPONSE_FIELD_NUMBER: _ClassVar[int] + ERRORS_FIELD_NUMBER: _ClassVar[int] + schema_response: SchemaResponse + attestation_response: AttestationResponse + errors: _containers.RepeatedCompositeFieldContainer[GraphQLError] + def __init__( + self, + schema_response: _Optional[_Union[SchemaResponse, _Mapping]] = ..., + attestation_response: _Optional[_Union[AttestationResponse, _Mapping]] = ..., + errors: _Optional[_Iterable[_Union[GraphQLError, _Mapping]]] = ..., + ) -> None: ... diff --git a/src/main/eas/v1/messages_pb2_grpc.py b/src/main/eas/v1/messages_pb2_grpc.py new file mode 100644 index 0000000..8a93939 --- /dev/null +++ b/src/main/eas/v1/messages_pb2_grpc.py @@ -0,0 +1,3 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc diff --git a/src/test/test_batch_attestation.py b/src/test/test_batch_attestation.py index 383ac70..6918383 100644 --- a/src/test/test_batch_attestation.py +++ b/src/test/test_batch_attestation.py @@ -10,9 +10,9 @@ import pytest -from main.EAS.core import EAS -from main.EAS.exceptions import EASTransactionError, EASValidationError -from main.EAS.transaction import TransactionResult +from eas import EAS +from eas.exceptions import EASTransactionError, EASValidationError +from eas.transaction import TransactionResult from .test_utils import requires_network, requires_private_key @@ -20,7 +20,7 @@ class TestBatchAttestation: """Unit tests for batch attestation functionality.""" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_empty_requests(self, mock_open, mock_web3_class): """Test multi_attest with empty requests list.""" @@ -36,7 +36,7 @@ def test_multi_attest_validation_empty_requests(self, mock_open, mock_web3_class ): eas.multi_attest([]) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_invalid_request_format( self, mock_open, mock_web3_class @@ -52,7 +52,7 @@ def test_multi_attest_validation_invalid_request_format( with pytest.raises(EASValidationError, match="Request 0 must be a dictionary"): eas.multi_attest(["invalid"]) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_invalid_schema_uid( self, mock_open, mock_web3_class @@ -89,7 +89,7 @@ def test_multi_attest_validation_invalid_schema_uid( ] ) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_empty_attestations( self, mock_open, mock_web3_class @@ -107,7 +107,7 @@ def test_multi_attest_validation_empty_attestations( ): eas.multi_attest([{"schema_uid": "0x" + "a" * 64, "attestations": []}]) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_invalid_recipient( self, mock_open, mock_web3_class @@ -131,7 +131,7 @@ def test_multi_attest_validation_invalid_recipient( ] ) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_validation_invalid_ref_uid(self, mock_open, mock_web3_class): """Test multi_attest with invalid ref_uid format.""" @@ -158,7 +158,7 @@ def test_multi_attest_validation_invalid_ref_uid(self, mock_open, mock_web3_clas ] ) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_success_single_schema(self, mock_open, mock_web3_class): """Test successful multi_attest with single schema.""" @@ -185,7 +185,7 @@ def test_multi_attest_success_single_schema(self, mock_open, mock_web3_class): mock_w3.eth.get_transaction_count.return_value = 1 # Mock signing and sending - with patch("main.EAS.core.Account.sign_transaction") as mock_sign: + with patch("main.eas.core.Account.sign_transaction") as mock_sign: mock_signed = Mock() mock_signed.rawTransaction = b"signed_tx" mock_sign.return_value = mock_signed @@ -260,7 +260,7 @@ def test_multi_attest_success_single_schema(self, mock_open, mock_web3_class): assert data2 == b"test_data_2" assert value2 == 1000 - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_success_multiple_schemas(self, mock_open, mock_web3_class): """Test successful multi_attest with multiple schemas.""" @@ -287,7 +287,7 @@ def test_multi_attest_success_multiple_schemas(self, mock_open, mock_web3_class) mock_w3.eth.get_transaction_count.return_value = 1 # Mock signing and sending - with patch("main.EAS.core.Account.sign_transaction") as mock_sign: + with patch("main.eas.core.Account.sign_transaction") as mock_sign: mock_signed = Mock() mock_signed.rawTransaction = b"signed_tx" mock_sign.return_value = mock_signed @@ -348,7 +348,7 @@ def test_multi_attest_success_multiple_schemas(self, mock_open, mock_web3_class) assert schema_uid2 == bytes.fromhex("b" * 64) assert len(attestation_data_list2) == 1 - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_gas_estimation_failure(self, mock_open, mock_web3_class): """Test multi_attest gas estimation failure.""" @@ -383,7 +383,7 @@ def test_multi_attest_gas_estimation_failure(self, mock_open, mock_web3_class): with pytest.raises(EASTransactionError, match="Build transaction failed"): eas.multi_attest(requests) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_attest_transaction_failure(self, mock_open, mock_web3_class): """Test multi_attest transaction failure.""" @@ -410,7 +410,7 @@ def test_multi_attest_transaction_failure(self, mock_open, mock_web3_class): mock_w3.eth.get_transaction_count.return_value = 1 # Mock signing and sending - with patch("main.EAS.core.Account.sign_transaction") as mock_sign: + with patch("main.eas.core.Account.sign_transaction") as mock_sign: mock_signed = Mock() mock_signed.rawTransaction = b"signed_tx" mock_sign.return_value = mock_signed diff --git a/src/test/test_eas.py b/src/test/test_eas.py index b499ade..bc1f8b5 100644 --- a/src/test/test_eas.py +++ b/src/test/test_eas.py @@ -5,7 +5,7 @@ import pytest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from main.EAS.core import EAS +from eas import EAS class TestEAS: @@ -36,7 +36,7 @@ def mock_contract(self): } return mock_contract - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open") @patch("json.load") def test_init_success( @@ -63,7 +63,7 @@ def test_init_success( assert eas.chain_id == 1 assert eas.contract_version == "0.26" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_init_connection_failure(self, mock_web3_class): """Test initialization failure when web3 connection fails""" # Setup mock to return False for is_connected @@ -85,7 +85,7 @@ def test_init_connection_failure(self, mock_web3_class): def test_get_offchain_uid_version_0(self, mock_web3, mock_contract): """Test get_offchain_uid with version 0""" with ( - patch("main.EAS.core.web3.Web3") as mock_web3_class, + patch("main.eas.core.web3.Web3") as mock_web3_class, patch("builtins.open"), patch("json.load") as mock_json_load, ): @@ -119,7 +119,7 @@ def test_get_offchain_uid_version_0(self, mock_web3, mock_contract): def test_get_offchain_uid_version_1(self, mock_web3, mock_contract): """Test get_offchain_uid with version 1""" with ( - patch("main.EAS.core.web3.Web3") as mock_web3_class, + patch("main.eas.core.web3.Web3") as mock_web3_class, patch("builtins.open"), patch("json.load") as mock_json_load, ): @@ -169,7 +169,7 @@ def test_get_offchain_uid_version_1(self, mock_web3, mock_contract): def test_get_offchain_uid_unsupported_version(self, mock_web3, mock_contract): """Test get_offchain_uid with unsupported version""" with ( - patch("main.EAS.core.web3.Web3") as mock_web3_class, + patch("main.eas.core.web3.Web3") as mock_web3_class, patch("builtins.open"), patch("json.load") as mock_json_load, ): diff --git a/src/test/test_foundation.py b/src/test/test_foundation.py index 023df6d..e0fd21e 100644 --- a/src/test/test_foundation.py +++ b/src/test/test_foundation.py @@ -8,14 +8,14 @@ import pytest -from main.EAS.exceptions import ( +from eas.exceptions import ( EASError, EASNetworkError, EASTransactionError, EASValidationError, ) -from main.EAS.observability import get_logger, log_operation -from main.EAS.transaction import TransactionResult +from eas.observability import get_logger, log_operation +from eas.transaction import TransactionResult from .test_utils import has_private_key, requires_network, requires_private_key @@ -193,9 +193,8 @@ def test_private_key_availability(self): @requires_network def test_transaction_result_with_real_receipt(self): """Test transaction result creation with real blockchain receipt format.""" - from main.EAS.core import EAS - from main.EAS.exceptions import EASValidationError - from main.EAS.transaction import TransactionResult + from eas import EAS + from eas.transaction import TransactionResult # Use environment variables for live testing rpc_url = os.getenv("RPC_URL", "https://sepolia.base.org") diff --git a/src/test/test_multi_chain_support.py b/src/test/test_multi_chain_support.py index 153e12b..458356e 100644 --- a/src/test/test_multi_chain_support.py +++ b/src/test/test_multi_chain_support.py @@ -6,13 +6,13 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from main.EAS.config import ( +from eas import EAS +from eas.config import ( get_mainnet_chains, get_network_config, get_testnet_chains, list_supported_chains, ) -from main.EAS.core import EAS class TestMultiChainSupport: @@ -116,9 +116,9 @@ def test_get_network_config_invalid_chain(self): ): get_network_config("non_existent_chain") - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_eas_from_chain_valid_chain(self, mock_web3_class): - """Test EAS.from_chain() with valid chain names""" + """Test eas.from_chain() with valid chain names""" # Mock web3 connection mock_w3 = MagicMock() mock_w3.is_connected.return_value = True @@ -139,9 +139,9 @@ def test_eas_from_chain_valid_chain(self, mock_web3_class): assert eas.contract_address is not None assert eas.from_account == test_from_account - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_eas_from_chain_with_overrides(self, mock_web3_class): - """Test EAS.from_chain() with custom RPC URL and contract address""" + """Test eas.from_chain() with custom RPC URL and contract address""" # Mock web3 connection mock_w3 = MagicMock() mock_w3.is_connected.return_value = True @@ -167,7 +167,7 @@ def test_eas_from_chain_with_overrides(self, mock_web3_class): assert eas.from_account == test_from_account def test_eas_from_chain_invalid_chain(self): - """Test EAS.from_chain() with invalid chain name""" + """Test eas.from_chain() with invalid chain name""" test_private_key = ( "0xa7c5ba7114b7119bb78dfc8e8ccd9f4ad8c6c9f2e8d7ab234fac8b1d5c7e9f12" ) @@ -180,7 +180,7 @@ def test_eas_from_chain_invalid_chain(self): EAS.from_chain("non_existent_chain", test_private_key, test_from_account) def test_eas_from_environment(self, mock_env_vars): - """Test EAS.from_environment() parsing""" + """Test eas.from_environment() parsing""" # Set environment variables os.environ["EAS_CHAIN"] = "polygon" os.environ["EAS_PRIVATE_KEY"] = ( @@ -188,14 +188,14 @@ def test_eas_from_environment(self, mock_env_vars): ) os.environ["EAS_FROM_ACCOUNT"] = "0xd796b20681bD6BEe28f0c938271FA99261c84fE8" - with patch("main.EAS.core.web3.Web3"): + with patch("main.eas.core.web3.Web3"): eas = EAS.from_environment() assert eas.chain_id is not None assert eas.from_account == os.environ["EAS_FROM_ACCOUNT"] def test_eas_from_environment_missing_vars(self, mock_env_vars): - """Test EAS.from_environment() with missing required variables""" + """Test eas.from_environment() with missing required variables""" # Clear all EAS-related environment variables for var in ["EAS_CHAIN", "EAS_PRIVATE_KEY", "EAS_FROM_ACCOUNT"]: os.environ.pop(var, None) @@ -203,7 +203,7 @@ def test_eas_from_environment_missing_vars(self, mock_env_vars): with pytest.raises(ValueError, match="Missing required environment variables"): EAS.from_environment() - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_backward_compatibility_factory_method(self, mock_web3_class): """Test that original create_eas_instance() works with new multi-chain support""" # Mock web3 connection @@ -211,7 +211,7 @@ def test_backward_compatibility_factory_method(self, mock_web3_class): mock_w3.is_connected.return_value = True mock_web3_class.return_value = mock_w3 - from main.EAS.config import create_eas_instance + from eas.config import create_eas_instance # Test with legacy network names (excluding deprecated ones that may fail security checks) legacy_networks = ["mainnet", "sepolia"] @@ -242,7 +242,7 @@ def test_backward_compatibility_factory_method(self, mock_web3_class): successful_tests >= 1 ), f"At least one legacy network should work, got {successful_tests}" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_multiple_eas_instances(self, mock_web3_class): """Test creating multiple EAS instances for different chains""" # Mock web3 connection @@ -273,7 +273,7 @@ def test_multiple_eas_instances(self, mock_web3_class): chains_to_test ), "Each chain should have a unique contract address" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") def test_performance_factory_methods(self, mock_web3_class): """Verify performance of factory methods""" import os diff --git a/src/test/test_offchain_revocation.py b/src/test/test_offchain_revocation.py index aa558f6..912c63c 100644 --- a/src/test/test_offchain_revocation.py +++ b/src/test/test_offchain_revocation.py @@ -10,8 +10,8 @@ import pytest -from main.EAS.core import EAS -from main.EAS.exceptions import EASValidationError +from eas import EAS +from eas.exceptions import EASValidationError from .test_utils import has_private_key, requires_network, requires_private_key @@ -19,7 +19,7 @@ class TestOffchainRevocation: """Unit tests for off-chain revocation functionality.""" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_revoke_offchain_validation(self, mock_open, mock_web3_class): """Test off-chain revocation input validation.""" @@ -43,7 +43,7 @@ def test_revoke_offchain_validation(self, mock_open, mock_web3_class): with pytest.raises(EASValidationError, match="Invalid attestation UID format"): eas.revoke_offchain("invalid-uid") - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_get_offchain_revocation_uid_version_0(self, mock_open, mock_web3_class): """Test off-chain revocation UID calculation for version 0.""" @@ -75,7 +75,7 @@ def test_get_offchain_revocation_uid_version_0(self, mock_open, mock_web3_class) assert uid == b"mock_uid" mock_w3.keccak.assert_called_once() - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_get_offchain_revocation_uid_version_1(self, mock_open, mock_web3_class): """Test off-chain revocation UID calculation for version 1 - now works with EIP-712 implementation.""" @@ -108,7 +108,7 @@ def test_get_offchain_revocation_uid_version_1(self, mock_open, mock_web3_class) assert isinstance(uid, bytes) assert len(uid) == 32 # 32 bytes - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_get_offchain_revocation_uid_invalid_version( self, mock_open, mock_web3_class @@ -134,10 +134,10 @@ def test_get_offchain_revocation_uid_invalid_version( ): eas.get_offchain_revocation_uid(message, version=99) - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) - @patch("main.EAS.core.os.urandom") - @patch("main.EAS.core.time.time") + @patch("main.eas.core.os.urandom") + @patch("main.eas.core.time.time") def test_revoke_offchain_success( self, mock_time, mock_urandom, mock_open, mock_web3_class ): @@ -180,7 +180,7 @@ def test_revoke_offchain_success( assert "data" in result assert result["revoker"] == eas.from_account - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_revoke_offchain_default_parameters(self, mock_open, mock_web3_class): """Test off-chain revocation with default parameters - currently blocked by EIP-712 issues.""" @@ -219,7 +219,7 @@ class TestOffchainRevocationIntegration: """Integration tests for off-chain revocation with network connectivity.""" @requires_network - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_offchain_revocation_structure(self, mock_open, mock_web3_class): """Test that off-chain revocation returns proper structure.""" @@ -251,8 +251,8 @@ def test_offchain_revocation_structure(self, mock_open, mock_web3_class): mock_signed_message.v = 28 mock_account.sign_message.return_value = mock_signed_message with patch("eth_account.Account.from_key", return_value=mock_account): - with patch("main.EAS.core.os.urandom", return_value=b"salt" * 8): - with patch("main.EAS.core.time.time", return_value=1234567890): + with patch("main.eas.core.os.urandom", return_value=b"salt" * 8): + with patch("main.eas.core.time.time", return_value=1234567890): result = eas.revoke_offchain( attestation_uid="0x" + "a" * 64, diff --git a/src/test/test_schema_generator.py b/src/test/test_schema_generator.py index 16ab4a1..9537447 100644 --- a/src/test/test_schema_generator.py +++ b/src/test/test_schema_generator.py @@ -4,7 +4,7 @@ import pytest -from main.EAS.schema_generator import ( +from eas.schema_generator import ( eas_to_protobuf_type, generate_eas_format, generate_json_format, @@ -63,7 +63,7 @@ def test_parse_eas_schema_definition_with_nested_arrays(self): def test_eas_to_protobuf_type_mapping(self): """Test EAS to protobuf type mapping.""" - from main.EAS.type_parser import EASType + from eas.type_parser import EASType assert eas_to_protobuf_type(EASType("address", [], False)) == "string" assert eas_to_protobuf_type(EASType("string", [], False)) == "string" @@ -79,7 +79,7 @@ def test_eas_to_protobuf_type_mapping(self): def test_generate_eas_format(self): """Test generating EAS format.""" - from main.EAS.type_parser import EASField, EASType + from eas.type_parser import EASField, EASType fields = [ EASField("domain", EASType("string", [], False)), @@ -93,7 +93,7 @@ def test_generate_eas_format(self): def test_generate_json_format(self): """Test generating JSON format.""" - from main.EAS.type_parser import EASField, EASType + from eas.type_parser import EASField, EASType fields = [ EASField("domain", EASType("string", [], False)), @@ -118,7 +118,7 @@ def test_generate_json_format(self): def test_generate_yaml_format(self): """Test generating YAML format.""" - from main.EAS.type_parser import EASField, EASType + from eas.type_parser import EASField, EASType fields = [ EASField("domain", EASType("string", [], False)), @@ -142,7 +142,7 @@ def test_generate_yaml_format(self): def test_generate_proto_format(self): """Test generating protobuf format.""" - from main.EAS.type_parser import EASField, EASType + from eas.type_parser import EASField, EASType fields = [ EASField("domain", EASType("string", [], False)), @@ -160,7 +160,7 @@ def test_generate_proto_format(self): def test_generate_proto_format_with_complex_types(self): """Test generating protobuf format with complex types (should fail).""" - from main.EAS.type_parser import EASField, EASType + from eas.type_parser import EASField, EASType fields = [ EASField("domain", EASType("string", [], False)), diff --git a/src/test/test_security_validation.py b/src/test/test_security_validation.py index 1cb9665..8c0bf02 100644 --- a/src/test/test_security_validation.py +++ b/src/test/test_security_validation.py @@ -15,7 +15,7 @@ import pytest -from src.main.EAS.security import ( +from eas.security import ( ContractAddressValidator, SecureEnvironmentValidator, SecurityError, @@ -100,7 +100,7 @@ def test_private_key_validation_success(self): # Use a private key with sufficient entropy for testing (not for real use!) valid_key = "0xa7c5ba7114b7119bb78dfc8e8ccd9f4ad8c6c9f2e8d7ab234fac8b1d5c7e9f12" - with patch("src.main.EAS.security.Account.from_key") as mock_account: + with patch("eas.security.Account.from_key") as mock_account: mock_account.return_value = MagicMock() with patch.object( SecureEnvironmentValidator, "_has_low_entropy", return_value=False @@ -132,7 +132,7 @@ def test_private_key_validation_failures(self): SecureEnvironmentValidator.validate_private_key("0x" + "12" * 33) # Weak keys - with patch("src.main.EAS.security.Account.from_key") as mock_account: + with patch("eas.security.Account.from_key") as mock_account: mock_account.return_value = MagicMock() weak_keys = [ @@ -146,7 +146,7 @@ def test_private_key_validation_failures(self): SecureEnvironmentValidator.validate_private_key(weak_key) # Cryptographically invalid keys - with patch("src.main.EAS.security.Account.from_key") as mock_account: + with patch("eas.security.Account.from_key") as mock_account: mock_account.side_effect = ValueError("Invalid key") with pytest.raises(SecurityError, match="Invalid private key"): @@ -169,9 +169,9 @@ def mock_checksum(addr): return addr.upper() with ( - patch("src.main.EAS.security.Web3.is_address", return_value=True), + patch("eas.security.Web3.is_address", return_value=True), patch( - "src.main.EAS.security.Web3.to_checksum_address", + "eas.security.Web3.to_checksum_address", side_effect=mock_checksum, ), ): @@ -204,7 +204,7 @@ def test_address_validation_failures(self): SecureEnvironmentValidator.validate_address("0x" + "12" * 21) # Web3 validation failure - with patch("src.main.EAS.security.Web3.is_address", return_value=False): + with patch("eas.security.Web3.is_address", return_value=False): with pytest.raises(SecurityError, match="Invalid Ethereum address"): SecureEnvironmentValidator.validate_address( "0x1234567890123456789012345678901234567890" @@ -449,14 +449,14 @@ def test_get_contract_type(self): class TestSecurityIntegration: """Integration tests for security validation in EAS factory methods""" - @patch("src.main.EAS.config.get_network_config") - @patch("src.main.EAS.config.validate_chain_config") - @patch("src.main.EAS.core.web3.Web3") + @patch("eas.config.get_network_config") + @patch("eas.config.validate_chain_config") + @patch("eas.core.web3.Web3") def test_eas_from_chain_security_validation( self, mock_web3, mock_validate, mock_get_config ): """Test security validation in EAS.from_chain method""" - from src.main.EAS.core import EAS + from eas import EAS # Mock configuration mock_config = { @@ -476,10 +476,10 @@ def test_eas_from_chain_security_validation( valid_address = "0xd796b20681bD6BEe28f0c938271FA99261c84fE8" with ( - patch("src.main.EAS.security.Account.from_key"), - patch("src.main.EAS.security.Web3.is_address", return_value=True), + patch("eas.security.Account.from_key"), + patch("eas.security.Web3.is_address", return_value=True), patch( - "src.main.EAS.security.Web3.to_checksum_address", + "eas.security.Web3.to_checksum_address", return_value=valid_address, ), patch.object( @@ -510,14 +510,14 @@ def test_eas_from_chain_security_validation( "EAS_FROM_ACCOUNT": "0xd796b20681bD6BEe28f0c938271FA99261c84fE8", }, ) - @patch("src.main.EAS.config.get_network_config") - @patch("src.main.EAS.config.validate_chain_config") - @patch("src.main.EAS.core.web3.Web3") + @patch("eas.config.get_network_config") + @patch("eas.config.validate_chain_config") + @patch("eas.core.web3.Web3") def test_eas_from_environment_security_validation( self, mock_web3, mock_validate, mock_get_config ): """Test security validation in EAS.from_environment method""" - from src.main.EAS.core import EAS + from eas import EAS # Mock configuration mock_config = { @@ -533,10 +533,10 @@ def test_eas_from_environment_security_validation( mock_w3.is_connected.return_value = True with ( - patch("src.main.EAS.security.Account.from_key"), - patch("src.main.EAS.security.Web3.is_address", return_value=True), + patch("eas.security.Account.from_key"), + patch("eas.security.Web3.is_address", return_value=True), patch( - "src.main.EAS.security.Web3.to_checksum_address", + "src.main.eas.security.Web3.to_checksum_address", return_value="0xd796b20681bD6BEe28f0c938271FA99261c84fE8", ), patch.object( @@ -557,7 +557,7 @@ def test_eas_from_environment_security_validation( ) def test_environment_injection_prevention(self): """Test that environment variable injection is prevented""" - from src.main.EAS.core import EAS + from eas import EAS with pytest.raises(ValueError, match="dangerous patterns"): EAS.from_environment() diff --git a/src/test/test_type_parser.py b/src/test/test_type_parser.py index 00e11f9..e90db1b 100644 --- a/src/test/test_type_parser.py +++ b/src/test/test_type_parser.py @@ -4,7 +4,7 @@ import pytest -from main.EAS.type_parser import ( +from eas.type_parser import ( EASField, EASType, EASTypeParser, diff --git a/src/test/test_write_operations.py b/src/test/test_write_operations.py index bcde35f..ea3c886 100644 --- a/src/test/test_write_operations.py +++ b/src/test/test_write_operations.py @@ -9,10 +9,10 @@ import pytest -from main.EAS.core import EAS -from main.EAS.exceptions import EASValidationError -from main.EAS.schema_registry import SchemaRegistry -from main.EAS.transaction import TransactionResult +from eas import EAS +from eas.exceptions import EASValidationError +from eas.schema_registry import SchemaRegistry +from eas.transaction import TransactionResult from .test_utils import has_private_key, requires_network, requires_private_key @@ -86,7 +86,7 @@ def test_get_registry_address(self): class TestEASWriteOperations: """Unit tests for new write operations in EAS class.""" - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_revoke_attestation_validation(self, mock_open, mock_web3_class): """Test attestation revocation input validation.""" @@ -110,7 +110,7 @@ def test_revoke_attestation_validation(self, mock_open, mock_web3_class): with pytest.raises(EASValidationError, match="Invalid attestation UID"): eas.revoke_attestation("invalid-uid") - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_revoke_validation(self, mock_open, mock_web3_class): """Test batch revocation input validation.""" @@ -135,7 +135,7 @@ def test_multi_revoke_validation(self, mock_open, mock_web3_class): with pytest.raises(EASValidationError, match="Missing UID"): eas.multi_revoke([{"value": 0}]) # Missing uid - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_timestamp_validation(self, mock_open, mock_web3_class): """Test timestamping input validation.""" @@ -159,7 +159,7 @@ def test_timestamp_validation(self, mock_open, mock_web3_class): with pytest.raises(EASValidationError, match="cannot be empty"): eas.timestamp(b"") - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_multi_timestamp_validation(self, mock_open, mock_web3_class): """Test batch timestamping input validation.""" @@ -190,7 +190,7 @@ class TestWriteOperationsIntegration: """Integration tests for write operations with network connectivity.""" @requires_network - @patch("main.EAS.core.web3.Web3") + @patch("main.eas.core.web3.Web3") @patch("builtins.open", new_callable=lambda: mock_file_content("[]")) def test_transaction_result_creation(self, mock_open, mock_web3_class): """Test that write operations return proper TransactionResult objects.""" @@ -215,7 +215,7 @@ def test_transaction_result_creation(self, mock_open, mock_web3_class): mock_w3.eth.get_transaction_count.return_value = 1 # Mock signing and sending - with patch("main.EAS.core.Account.sign_transaction") as mock_sign: + with patch("main.eas.core.Account.sign_transaction") as mock_sign: mock_signed = Mock() mock_signed.rawTransaction = b"signed_tx" mock_sign.return_value = mock_signed