Skip to content

python: zap_py schema DSL (StructBuilder, predefined EVM schemas)#3

Open
abhicris wants to merge 3 commits intoluxfi:mainfrom
kcolbchain:zap-py-schema
Open

python: zap_py schema DSL (StructBuilder, predefined EVM schemas)#3
abhicris wants to merge 3 commits intoluxfi:mainfrom
kcolbchain:zap-py-schema

Conversation

@abhicris
Copy link
Copy Markdown

Summary

Closes the not-yet-ported list from #1 and #2. Ports the schema DSL so Python code can declare a struct shape, get computed field offsets, and stay byte-compatible with Go-declared schemas.

  • Type IntEnum (matches schema.go iota), type_size() helper.
  • Field, Struct, Enum, Schema dataclasses with a registry (new_schema, add_struct, add_enum).
  • Fluent StructBuilder with the full Go surface — bool/int8–64/uint8–64/float32–64/text/bytes/list/struct — plus the EVM extension methods address/hash/signature. Same alignment rules as schema.go: each scalar rounds to its width, EVM types use no alignment, build() final-aligns to 8.
  • Predefined schemas — TRANSACTION_SCHEMA, BLOCK_HEADER_SCHEMA, LOG_SCHEMA — declared exactly the way evm.go declares them.

Cross-language parity

gen_schema_fixture.go dumps Go's offsets and sizes for all three predefined schemas as JSON. The Python parity test loads it and asserts every field offset and total size match:

go run ./python/testdata/gen_schema_fixture.go > /tmp/zap_schemas.json
ZAP_GO_SCHEMA_FIXTURE=/tmp/zap_schemas.json python -m pytest python/tests/test_schema.py::test_go_schema_fixture_parity

Result on this branch: byte-identical layout for Transaction (size 192), BlockHeader (280), and Log. Anything declared with StructBuilder on either side is now interchangeable.

Stack

GitHub will rebase/squash cleanly once #1 and #2 land. Until then, the diff is best read as additions on top of zap-py-evm.

Test plan

  • cd python && .venv/bin/pytest tests/ — 35 passed, 3 skipped (interop fixtures gated on env vars)
  • All three Go-built fixtures (roundtrip, EVM, schemas) parsed/matched from Python
  • go test ./... — full Go suite still green

Abhishek Krishna added 3 commits April 27, 2026 17:41
Adds python/zap_py — a pure-stdlib (no deps, py>=3.8) reader and
builder that's wire-compatible with the Go reference. Lets non-Go
peers (AI agents, ops scripts, FHE clients) speak ZAP without
leaving Python.

Read: parse() returns a Message backed by a memoryview; .bytes_view()
exposes zero-copy slices.
Build: Builder/ObjectBuilder/ListBuilder mirror builder.go method
for method, including the deferred-write trick for text/bytes so
relative offsets match the Go encoder byte for byte.

Coverage: header validation, all scalar widths (bool, u8/16/32/64,
i8/16/32/64, f32/f64), text, bytes, nested objects, lists of
u8/u32/u64/objects/raw bytes, null pointers, flag bits.

Bidirectional fixtures prove parity:
- python/testdata/gen_fixture.go      → emits a Go-built message
- python/testdata/gen_python_fixture.py → emits a Python-built one
- python/tests/test_roundtrip.py::test_go_fixture_interop reads
  the Go fixture from Python (skipped unless ZAP_GO_FIXTURE is set)
- python_interop_test.go::TestPythonFixture reads the Python fixture
  from Go (skipped unless ZAP_PYTHON_FIXTURE is set)

Both fixtures use identical schemas; output is byte-identical
except for the embedded text payload.

go.sum gains the missing luxfi/mdns line that `go test ./...` already
requires (fixes a tidy-state issue surfaced while wiring up the new
Go interop test).

Not ported (yet): EVM helpers, MCP bridge, mDNS node, schema DSL.
Reader+builder is enough to interop with any Go ZAP service that
publishes a fixed schema; higher-level helpers can follow as Python
use cases land.
Follow-up to luxfi#1. Adds the EVM helpers called out as not-yet-ported in
that PR: fixed-width Address (20), Hash (32), Signature (65), Bloom
(256), with hex parsing, zero detection, and reader/builder methods
on Object, ObjectBuilder, and List that mirror evm.go.

New module: python/zap_py/evm.py
  - _FixedBytes base + Address / Hash / Signature / Bloom
  - .from_hex() (0x/0X tolerated), .hex(), .is_zero(), .bytes
  - bytes-equality so Address == bytes(...) works
  - ZERO_ADDRESS / ZERO_HASH constants
  - top-level address_from_hex / hash_from_hex helpers

Reader extensions (reader.py):
  - Object.address / .hash / .signature
  - Object.address_slice / .hash_slice (zero-copy memoryview)
  - List.address / .hash for lists of fixed-width elements

Builder extensions (builder.py):
  - ObjectBuilder.set_address / .set_hash / .set_signature
  - All accept either the typed value or raw bytes; reject mismatched
    lengths with a clean ValueError

Tests (13 new): hex roundtrip (case-insensitive prefix), invalid-length
rejection, zero detection, single-field roundtrip, zero-copy slice
view, raw-bytes-input acceptance, address-list and hash-list typed
access, bytes equality.

Cross-language parity: python/testdata/gen_evm_fixture.go emits a
Go-built EVM-typed message; tests/test_evm.py::test_evm_go_fixture_interop
parses it from Python under ZAP_GO_EVM_FIXTURE.
Closes the "not yet ported" list from luxfi#1 and luxfi#2. Adds the schema DSL
that lets Python declare a struct shape, get computed field offsets,
and stay byte-compatible with Go-declared schemas.

New module: python/zap_py/schema.py
  - Type IntEnum (matches schema.go's iota ordering)
  - type_size(t) helper
  - Field / Struct / Enum / Schema dataclasses
  - new_schema, new_struct_builder convenience functions
  - StructBuilder with the full Go API:
      bool/int8-64/uint8-64/float32-64/text/bytes/list/struct
      + EVM hooks: address (20), hash (32), signature (65)
  - Same alignment rules as schema.go: each scalar rounds to its
    width, EVM types use no alignment, build() final-aligns to 8
  - Predefined schemas (1:1 with evm.go):
      TRANSACTION_SCHEMA, BLOCK_HEADER_SCHEMA, LOG_SCHEMA

Tests (11 new): per-type sizes, mixed-width alignment math, no
unnecessary padding for same-width chains, field lookup, list-elem
type tracking, struct-name carry-through, schema registry, EVM
extension offsets, hand-computed Transaction layout (size 192),
schema-driven roundtrip with the Builder.

Cross-language parity: gen_schema_fixture.go dumps Go's offsets and
sizes for the three predefined schemas as JSON. Python loads it back
and asserts every field offset and the total size match — both sides
compute exactly the same layout for Transaction (192), BlockHeader
(280), and Log.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant