Skip to content

Commit 3ff0f5c

Browse files
author
Matthias Zimmermann
committed
simplify Annotations, refactorings
1 parent 9893c9c commit 3ff0f5c

File tree

9 files changed

+358
-298
lines changed

9 files changed

+358
-298
lines changed

src/arkiv/account.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ def main() -> None:
175175
password = getpass.getpass("Enter wallet password: ")
176176
encrypted = account.local_account.encrypt(password)
177177

178+
# Ensure address has 0x prefix (eth_account.encrypt doesn't include it)
179+
if "address" in encrypted and not encrypted["address"].startswith("0x"):
180+
encrypted["address"] = "0x" + encrypted["address"]
181+
178182
with wallet_path.open("w") as f:
179183
json.dump(encrypted, f)
180184

src/arkiv/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ class EntityKeyException(Exception):
55
pass
66

77

8+
class AnnotationException(Exception):
9+
pass
10+
11+
812
class AccountNameException(Exception):
913
pass
1014

src/arkiv/module.py

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,32 @@
44
import logging
55
from typing import TYPE_CHECKING, Any
66

7+
from eth_typing import ChecksumAddress
78
from hexbytes import HexBytes
89
from web3 import Web3
910
from web3.types import TxParams, TxReceipt
1011

1112
from .contract import EVENTS_ABI, FUNCTIONS_ABI, STORAGE_ADDRESS
1213
from .types import (
13-
AnnotationValue,
14+
ALL,
15+
ANNOTATIONS,
16+
METADATA,
17+
PAYLOAD,
18+
Annotations,
19+
CreateOp,
1420
Entity,
1521
EntityKey,
16-
Metadata,
1722
Operations,
1823
TransactionReceipt,
1924
)
20-
from .utils import merge_annotations, to_create_operation, to_receipt, to_tx_params
25+
from .utils import merge_annotations, to_receipt, to_tx_params
2126

2227
# Deal with potential circular imports between client.py and module.py
2328
if TYPE_CHECKING:
2429
from .client import Arkiv
2530

2631
logger = logging.getLogger(__name__)
2732

28-
PAYLOAD = 1
29-
ANNOTATIONS = 2
30-
METADATA = 4
31-
ALL = PAYLOAD | ANNOTATIONS | METADATA
32-
3333
TX_SUCCESS = 1
3434

3535

@@ -57,7 +57,7 @@ def is_available(self) -> bool:
5757
def create_entity(
5858
self,
5959
payload: bytes | None = None,
60-
annotations: dict[str, AnnotationValue] | None = None,
60+
annotations: Annotations | None = None,
6161
btl: int = 0,
6262
tx_params: TxParams | None = None,
6363
) -> tuple[EntityKey, HexBytes]:
@@ -73,10 +73,14 @@ def create_entity(
7373
Returns:
7474
Transaction hash of the create operation
7575
"""
76+
# Check and set defaults
77+
if not payload:
78+
payload = b""
79+
if not annotations:
80+
annotations = Annotations({})
81+
7682
# Create the operation
77-
create_op = to_create_operation(
78-
payload=payload, annotations=annotations, btl=btl
79-
)
83+
create_op = CreateOp(payload=payload, annotations=annotations, btl=btl)
8084

8185
# Wrap in Operations container
8286
operations = Operations(creates=[create_op])
@@ -118,9 +122,10 @@ def get_entity(self, entity_key: EntityKey, fields: int = ALL) -> Entity:
118122
Entity object with the requested fields
119123
"""
120124
# Gather the requested data
121-
payload = None
122-
metadata = None
123-
annotations = None
125+
owner: ChecksumAddress | None = None
126+
expires_at_block: int | None = None
127+
payload: bytes | None = None
128+
annotations: Annotations | None = None
124129

125130
# HINT: rpc methods to fetch entity content might change this is the place to adapt
126131
# get and decode payload if requested
@@ -133,21 +138,11 @@ def get_entity(self, entity_key: EntityKey, fields: int = ALL) -> Entity:
133138

134139
if fields & METADATA:
135140
# Convert owner address to checksummed format
136-
owner_address = metadata_all.get("owner")
137-
if not owner_address:
138-
raise ValueError("Entity metadata missing required owner field")
139-
checksummed_owner = Web3.to_checksum_address(owner_address)
140-
141-
expires_at_block = metadata_all.get("expiresAtBlock")
142-
if expires_at_block is None:
143-
raise ValueError(
144-
"Entity metadata missing required expiresAtBlock field"
145-
)
146-
147-
metadata = Metadata(
148-
owner=checksummed_owner,
149-
expires_at_block=int(expires_at_block),
150-
)
141+
owner_metadata = metadata_all.get("owner")
142+
owner = Web3.to_checksum_address(owner_metadata)
143+
144+
expires_at_block_metadata = metadata_all.get("expiresAtBlock")
145+
expires_at_block = int(expires_at_block_metadata)
151146

152147
if fields & ANNOTATIONS:
153148
annotations = merge_annotations(
@@ -158,8 +153,10 @@ def get_entity(self, entity_key: EntityKey, fields: int = ALL) -> Entity:
158153
# Create and return entity
159154
return Entity(
160155
entity_key=entity_key,
156+
fields=fields,
157+
owner=owner,
158+
expires_at_block=expires_at_block,
161159
payload=payload,
162-
metadata=metadata,
163160
annotations=annotations,
164161
)
165162

src/arkiv/types.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,57 @@
66

77
from eth_typing import ChecksumAddress, HexStr
88
from hexbytes import HexBytes
9+
from web3.datastructures import AttributeDict
910

10-
# Unique key for all entities
11-
EntityKey = NewType("EntityKey", HexStr)
12-
13-
14-
type AnnotationValue = str | int # Only str or non-negative int allowed
15-
16-
17-
@dataclass(frozen=True)
18-
class Annotation:
19-
"""Class to represent annotations with string or non-negative integer values."""
20-
21-
key: str
22-
value: AnnotationValue
23-
24-
def __post_init__(self) -> None:
25-
"""Validate that integer values are non-negative."""
26-
if isinstance(self.value, int) and self.value < 0:
27-
raise ValueError(
28-
f"Integer annotation values must be non-negative, got: {self.value}"
29-
)
30-
31-
# @override
32-
def __repr__(self) -> str:
33-
"""Encode annotation as a string."""
34-
return f"{type(self).__name__}({self.key} -> {self.value})"
11+
# Field bitmask values to specify which entity fields are populated
12+
PAYLOAD = 1
13+
ANNOTATIONS = 2
14+
METADATA = 4
15+
ALL = PAYLOAD | ANNOTATIONS | METADATA
3516

3617

37-
@dataclass(frozen=True)
38-
class Metadata:
39-
"""A class representing entity metadata."""
18+
# Unique key for all entities
19+
EntityKey = NewType("EntityKey", HexStr)
4020

41-
owner: ChecksumAddress
42-
expires_at_block: int
21+
# Entity annotations
22+
Annotations = NewType("Annotations", dict[str, str | int])
4323

4424

4525
@dataclass(frozen=True)
4626
class Entity:
4727
"""A class representing an entity."""
4828

49-
entity_key: EntityKey
50-
metadata: Metadata | None
29+
entity_key: EntityKey # Unique identifier for the entity
30+
fields: int # Bitmask representing which fields are populated
31+
32+
# Populated when fields | METADATA returns true
33+
owner: ChecksumAddress | None
34+
expires_at_block: int | None
35+
36+
# Populated when fields | PAYLOAD returns true
5137
payload: bytes | None
52-
annotations: dict[str, AnnotationValue] | None
38+
39+
# Populated when fields | ANNOTATIONS returns true
40+
annotations: Annotations | None
5341

5442

5543
@dataclass(frozen=True)
5644
class CreateOp:
5745
"""Class to represent a create operation."""
5846

59-
data: bytes
47+
payload: bytes
48+
annotations: Annotations
6049
btl: int
61-
string_annotations: Sequence[Annotation]
62-
numeric_annotations: Sequence[Annotation]
6350

6451

6552
@dataclass(frozen=True)
6653
class UpdateOp:
6754
"""Class to represent an update operation."""
6855

6956
entity_key: EntityKey
70-
data: bytes
57+
payload: bytes
58+
annotations: Annotations
7159
btl: int
72-
string_annotations: Sequence[Annotation]
73-
numeric_annotations: Sequence[Annotation]
7460

7561

7662
@dataclass(frozen=True)
@@ -164,3 +150,12 @@ class TransactionReceipt:
164150
updates: Sequence[UpdateReceipt]
165151
extensions: Sequence[ExtendReceipt]
166152
deletes: Sequence[DeleteReceipt]
153+
154+
155+
# Low level annotations for RLP encoding
156+
StringAnnotationsRlp = NewType("StringAnnotationsRlp", list[tuple[str, str]])
157+
NumericAnnotationsRlp = NewType("NumericAnnotationsRlp", list[tuple[str, int]])
158+
159+
# Low level annotations for entity decoding
160+
StringAnnotations = NewType("StringAnnotations", AttributeDict[str, str])
161+
NumericAnnotations = NewType("NumericAnnotations", AttributeDict[str, int])

0 commit comments

Comments
 (0)