feat: BCS ABNF compile-time generation#1040
feat: BCS ABNF compile-time generation#1040thibault-martinez wants to merge 25 commits intodevelopfrom
Conversation
| #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] | ||
| #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] | ||
| #[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))] | ||
| #[cfg_attr(feature = "bcs-schema", bcs_schema(definition = "%x60 96OCTET"))] |
There was a problem hiding this comment.
can these be merged? If yes, do it for all instances please
Thoralf-M
left a comment
There was a problem hiding this comment.
We should add a README.md in iota-bcs-schema/ which explains what this is good for and how to use it
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(definition = "%x60 96OCTET") |
There was a problem hiding this comment.
Can you check if "OCTET" is the standard?
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(definition = "%x60 96OCTET") |
There was a problem hiding this comment.
Can we have it in decimal?
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(definition = "bytes") |
There was a problem hiding this comment.
What is bytes? Is it defined in the ABNF? length-prefixed?
There was a problem hiding this comment.
There are now definitions added to the file for these basic types
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(definition = "string") |
There was a problem hiding this comment.
Properly define string in the ABNF?
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(name = "type-tag") |
There was a problem hiding this comment.
What would happen if I created a different type with the same BCS schema name? Can we make sure it fails?
There was a problem hiding this comment.
There is now duplicate detection
| #[cfg_attr( | ||
| feature = "bcs-schema", | ||
| derive(iota_bcs_schema::BcsSchema), | ||
| bcs_schema(definition = "32OCTET") |
There was a problem hiding this comment.
Can we put a space? I always read it as 320 :(
There was a problem hiding this comment.
I could add something like byte = OCTET so it would look like 32byte instead?
|
|
||
| #[derive(serde::Serialize, serde::Deserialize)] | ||
| #[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))] | ||
| #[cfg_attr(feature = "bcs-schema", bcs_schema(name = "genesis-object"))] |
Summary
This PR introduces a machine-readable BCS schema for
iota-sdk-types, expressed as an ABNF grammar file, along with the tooling to generate and validate it.What's included
crates/iota-bcs-schema— a new proc-macro crate providing a#[derive(BcsSchema)]macro. When applied to a type, it emits abcs_schema_definition()method that returns the type's ABNF grammar fragment. The macro handles structs, enums, and common generic wrappers (Option<T>,Vec<T>,HashMap<K, V>), and supports override attributes (#[bcs_schema(name = "...", definition = "...")]on types and#[bcs_schema(as_type = "...")]on fields/variants) for cases where the derived output needs manual tuning.crates/iota-sdk-types/bcs-schema.abnf— the auto-generated ABNF schema covering all SDK types (transactions, effects, checkpoints, crypto primitives, etc.). It is committed to the repo and regenerated on demand viaBCS_SCHEMA=1 cargo check -p iota-sdk-types --features bcs-schema.crates/iota-sdk-types/build.rs— a build script that, whenBCS_SCHEMA=1is set alongside thebcs-schemafeature, deletes the old schema file and touches all.rssources to force every#[derive(BcsSchema)]to re-run, producing a fresh schema from scratch.Grammar-driven fuzz tests (
tests/bcs_schema_fuzzing.rs) — a test suite that parsesbcs-schema.abnf, randomly generates conforming byte sequences for each rule, and asserts that the BCS deserializer accepts them. This validates that the grammar is a sound description of the wire format.CI — a new lint workflow job that regenerates the schema and fails if the committed file is out of date.
Motivation
Having a formal, human- and machine-readable description of the BCS wire format makes it easier to:
Usage
Derive attributes
#[bcs_schema(definition = "32OCTET")][u8; Self::LENGTH], etc.)#[bcs_schema(name = "custom-name")]#[bcs_schema(as_type = "u64")]Version = u64)#[bcs_schema(as_type = "call-arg")]#[bcs_schema(skip)]Known limitations
as_typeanddefinitionvalues are trusted — no validation against the actual serde implSerializeimpls are invisible to the macro; divergences must be annotated manuallyskip,rename,flatten) are not read by the macroTest plan
cargo check -p iota-sdk-types --all-features— no regressionsBCS_SCHEMA=1 cargo check --features bcs-schemaproduces complete, sortedbcs-schema.abnfBCS_SCHEMA=1builds produce byte-identical outputBcsSchemafrom a field type causes a compile errorcargo checkwithout the feature is unaffected (fully incremental)