Skip to content

Commit 588fa62

Browse files
author
Matthias Zimmermann
committed
refactor entity update tests, amend readme, add USE_CASES.md
1 parent 36a6cfc commit 588fa62

File tree

4 files changed

+730
-92
lines changed

4 files changed

+730
-92
lines changed

README.md

Lines changed: 195 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ alice = NamedAccount.create('Alice')
3232
client = Arkiv(account=alice)
3333
print(f"Connected: {client.is_connected()}")
3434

35-
# Fund the account
36-
client.node.fund_account(alice)
37-
print(f"Balance: {client.eth.get_balance(alice.address)}")
38-
3935
# Create entity with data and annotations
4036
entity_key, tx_hash = client.arkiv.create_entity(
4137
payload=b"Hello World!",
@@ -45,18 +41,48 @@ entity_key, tx_hash = client.arkiv.create_entity(
4541

4642
# Check and print entity key
4743
exists = client.arkiv.entity_exists(entity_key)
48-
print(f"Created entity: {entity_key}, exists={exists}")
44+
print(f"Created entity: {entity_key} (exists={exists}), creation TX: {tx_hash}")
4945

5046
# Get individual entity and print its details
5147
entity = client.arkiv.get_entity(entity_key)
5248
print(f"Entity: {entity}")
5349

54-
# TODO
55-
# Clean up - delete entities
50+
# Clean up - delete entity
5651
client.arkiv.delete_entity(entity_key)
57-
print("Entities deleted")
52+
print("Entity deleted")
53+
```
54+
55+
### Web3 Standard Support
56+
```python
57+
from web3 import HTTPProvider
58+
provider = HTTPProvider('https://kaolin.hoodi.arkiv.network/rpc')
59+
60+
# Arkiv 'is a' Web3 client
61+
client = Arkiv(provider)
62+
balance = client.eth.get_balance(client.eth.default_account)
63+
tx = client.eth.get_transaction(tx_hash)
64+
```
65+
66+
### Arkiv Module Extension
67+
```python
68+
from arkiv import Arkiv
69+
from arkiv.account import NamedAccount
70+
71+
account = NamedAccount.from_wallet('Alice', wallet, 's3cret')
72+
client = Arkiv(provider, account = account)
73+
74+
entity_key, tx_hash = client.arkiv.create_entity(
75+
payload=b"Hello World!",
76+
annotations={"type": "greeting", "version": 1},
77+
btl = 1000
78+
)
79+
80+
entity = client.arkiv.get_entity(entity_key)
81+
exists = client.arkiv.exists(entity_key)
5882
```
5983

84+
## Advanced Features
85+
6086
### Provider Builder
6187

6288
The snippet below demonstrates the creation of various nodes to connect to using the `ProviderBuilder`.
@@ -82,35 +108,179 @@ provider_kaolin_ws = ProviderBuilder().kaolin().ws().build()
82108
provider_custom = ProviderBuilder().custom("https://my-rpc.io").build()
83109
```
84110

85-
### Web3 Standard Support
111+
## Arkiv Topics/Features
112+
113+
### Deprecate BTL
114+
115+
BTL (Blocks-To-Live) should be replaced with explicit `expires_at_block` values for predictability and composability.
116+
117+
Relative `BTL` depends on execution timing and creates unnecessary complexity:
118+
- An entity created with `btl=100` will have different expiration blocks depending on when the transaction is mined
119+
- Extending entity lifetimes requires fetching the entity, calculating remaining blocks, and adding more—a race-prone pattern
120+
- Creates asymmetry between write operations (which use `btl`) and read operations (which return `expires_at_block`)
121+
122+
Absolute `expires_at_block` is predictable, composable, and matches what you get when reading entities:
123+
- Deterministic regardless of execution timing
124+
- Maps directly to `Entity.expires_at_block` field returned by queries
125+
- Enables clean compositional patterns like `replace(entity, expires_at_block=entity.expires_at_block + 100)`
126+
- Aligns write API with read API, making the SDK more intuitive
127+
128+
With `expires_at_block`, updating entities becomes cleaner:
129+
86130
```python
87-
from web3 import HTTPProvider
88-
provider = HTTPProvider('https://kaolin.hoodi.arkiv.network/rpc')
131+
from dataclasses import replace
89132

90-
# Arkiv 'is a' Web3 client
91-
client = Arkiv(provider)
92-
balance = client.eth.get_balance(client.eth.default_account)
93-
tx = client.eth.get_transaction(tx_hash)
133+
# Fetch entity
134+
entity = client.arkiv.get_entity(entity_key)
135+
136+
# Modify payload and extend expiration by 100 blocks
137+
updated_entity = replace(
138+
entity,
139+
payload=b"new data",
140+
expires_at_block=entity.expires_at_block + 100
141+
)
142+
143+
# Update entity
144+
client.arkiv.update_entity(updated_entity)
94145
```
95146

96-
### Arkiv Module Extension
147+
### Query DSL
148+
149+
To make querying entities as simple and natural as possible, rely on a suitable and existing query DSL. Since Arkiv currently uses a SQL database backend and is likely to support SQL databases in the future, the Arkiv query DSL is defined as a **subset of the SQL standard**.
150+
151+
**Rationale:**
152+
- Leverages existing SQL knowledge - no new language to learn
153+
- Well-defined semantics and broad tooling support
154+
- Natural fit for relational data structures
155+
- Enables familiar filtering, joining, and aggregation patterns
156+
157+
**Example:**
97158
```python
98-
from arkiv import Arkiv
99-
from arkiv.account import NamedAccount
159+
# Query entities using SQL-like syntax
160+
results = client.arkiv.query(
161+
"SELECT entity_key, payload WHERE annotations.type = 'user' AND annotations.age > 18 ORDER BY annotations.name"
162+
)
163+
```
100164

101-
account = NamedAccount.from_wallet('Alice', wallet, 's3cret')
102-
client = Arkiv(provider, account = account)
165+
### Paging
103166

104-
entity_key, tx_hash = client.arkiv.create_entity(
105-
payload=b"Hello World!",
106-
annotations={"type": "greeting", "version": 1},
107-
btl = 1000
167+
Paging of query results is currently in development.
168+
169+
**Requirements:**
170+
- Support cursor-based pagination for consistent results
171+
- Configurable maximum page size (a page might contain fewer entities depending on actual data used for the representation per entity)
172+
- Return page metadata (total count, has_next_page, cursor)
173+
174+
**Example:**
175+
```python
176+
# Fetch first page
177+
page = client.arkiv.query("SELECT * FROM entities", max_page_size=100)
178+
179+
# Fetch next page using cursor
180+
next_page = client.arkiv.query("SELECT * FROM entities", cursor=page.next, max_page_size=100)
181+
```
182+
183+
### Sorting
184+
185+
Querying entities should support sorting results by one or more fields.
186+
187+
**Requirements:**
188+
- Sort by annotations (string and numeric)
189+
- Sort by metadata (owner, expires_at_block)
190+
- Support ascending and descending order
191+
- Multi-field sorting with priority
192+
193+
**Example:**
194+
```python
195+
# SQL-style sorting
196+
results = client.arkiv.query(
197+
"SELECT * FROM entities ORDER BY annotations.priority DESC, annotations.name ASC"
108198
)
199+
```
200+
201+
### Projections
202+
203+
The transfer of large entities or many entities consumes considerable bandwidth. Which information per entity is most valuable is use-case specific and should be specified by the application.
204+
205+
**Let users decide which parts of an entity to return:**
206+
- **Payload** - Binary data (can be large)
207+
- **Annotations** - Key-value metadata
208+
- **Metadata** - Owner, expiration, timestamps
209+
210+
**Current implementation:**
211+
The SDK already supports projections via the `fields` parameter using bitmask flags:
212+
```python
213+
from arkiv.types import PAYLOAD, ANNOTATIONS, METADATA
214+
215+
# Fetch only annotations (minimal bandwidth)
216+
entity = client.arkiv.get_entity(entity_key, fields=ANNOTATIONS)
217+
218+
# Fetch payload and metadata (skip annotations)
219+
entity = client.arkiv.get_entity(entity_key, fields=PAYLOAD | METADATA)
109220

221+
# Fetch everything (fields = ALL is default)
110222
entity = client.arkiv.get_entity(entity_key)
111-
exists = client.arkiv.exists(entity_key)
112223
```
113224

225+
### Entity Existence Check
226+
227+
Make testing whether an entity exists for a specific entity key as efficient as possible.
228+
229+
**Current implementation:**
230+
```python
231+
exists = client.arkiv.entity_exists(entity_key) # Returns bool
232+
```
233+
234+
**Options for optimization:**
235+
- Dedicated lightweight RPC endpoint (current approach)
236+
- Fold into unified query RPC with minimal projection
237+
- Support batch existence checks for multiple keys
238+
239+
**Example batch API:**
240+
```python
241+
# Check multiple entities at once
242+
existence_map = client.arkiv.entities_exist([key1, key2, key3])
243+
# Returns: {key1: True, key2: False, key3: True}
244+
```
245+
246+
### Other Features
247+
248+
- **Ownership Transfer**: The creating account is the owner of the entity.
249+
Only the owner can update the entity (payload, annotations, expires_at_block).
250+
A mechanism to transfer entity ownership should be provided.
251+
```python
252+
# Proposed API
253+
client.arkiv.transfer_entity(entity_key, new_owner_address)
254+
```
255+
256+
- **Creation Flags**: Entities should support creation-time flags with meaningful defaults.
257+
Flags can only be set at creation and define entity behavior:
258+
- **Read-only**: Once created, entity data cannot be changed by anyone (immutable)
259+
- **Unpermissioned extension**: Entity lifetime can be extended by anyone, not just the owner
260+
```python
261+
# Proposed API
262+
client.arkiv.create_entity(
263+
payload=b"data",
264+
annotations={"type": "public"},
265+
expires_at_block=future_block,
266+
flags=EntityFlags.READ_ONLY | EntityFlags.PUBLIC_EXTENSION
267+
)
268+
```
269+
270+
- **ETH Transfers**: Arkiv chains should support ETH (or native token like GLM) transfers for gas fees and value transfer.
271+
```python
272+
# Already supported via Web3.py compatibility
273+
tx_hash = client.eth.send_transaction({
274+
'to': recipient_address,
275+
'value': client.to_wei(1, 'ether'),
276+
'gas': 21000
277+
})
278+
```
279+
280+
- **Offline Entity Verification**: Provide cryptographic verification of entity data without querying the chain.
281+
- Signature verification for entity authenticity
282+
- Minimal trust assumptions for light clients
283+
114284
## Development Guide
115285

116286
### Branches, Versions, Changes

0 commit comments

Comments
 (0)