Skip to content

Adapting to core's lineage rewrite is 20 files, and one of them is an untested public regression #229

Description

@LKSNDRTMLKV

dpp-core is changing both passport lineage edges. The work here is larger than
a version bump, and one part of it is a silent, untested regression on a
public scan-facing surface
that would not be caught by either repo's CI.

Filed ahead of the release so the scope is known before the repin, not during it.

Blocked on odal-node/dpp-core PRs #214#217 merging and 0.20.0 being cut.
All four are green and MERGEABLE, but nothing here should start until the
version exists — and the release is deliberately not imminent.

What changes in core

Before After
parent_passport_ref: Option<PassportRef> derived_from: Vec<DerivationRef>
wire key parentPassportRef wire key derivedFrom
component_refs: Vec<PassportRef> component_refs: Vec<ComponentRef>
key componentRefs unchanged; elements are now objects

DerivationRef is { reference, operation } where operation is one of the
four Reg. (EU) 2023/1542 Art. 77(7) second-life operations, required.
ComponentRef is { reference, quantity?, role? }.

Core ships a tolerant reader for the downward edge. ComponentRef
deserializes the bare PassportRef element shape as well, mapping it to an edge
with no qualifiers. That exists because componentRefs is in the signed public
view
that other operators' nodes fetch — those passports are signed and belong
to someone else, so they can never be rewritten, and refusing them would report a
not-yet-upgraded node's bill of materials as an integrity violation.

That tolerance only reaches consumers that deserialize into ComponentRef.
Anything hand-navigating serde_json::Value gets nothing from it. See the
resolver item below.

Scope: 20 non-test source files

Counted with git grep -l 'parent_passport_ref\|component_refs\|parentPassportRef\|componentRefs' -- '*.rs' on main, excluding tests. Roughly 150
references across .rs, .yaml and .json.

Compiler-driven — the type change finds these for you

  • crates/dpp-integrator/src/domain/validate/{aluminium,battery,steel,textile,tyre}.rs
  • crates/dpp-integrator/src/infra/vault_client.rs
  • crates/dpp-vault/src/domain/service/{mod,create,publish}.rs
  • crates/dpp-vault/src/public_view.rs
  • test fixtures across dpp-vault, dpp-node, dpp-dal

Mostly parent_passport_ref: Nonederived_from: Vec::new() and the
equivalent for component refs.

Needs judgement

crates/dpp-vault/src/domain/verify/tree.rsverify_and_children parses
each fetched entry with serde_json::from_value::<PassportRef>. Switching that
to ComponentRef is what inherits core's tolerance; leaving it produces
RefUnverifiable::MalformedRef for every entry of a passport published by a node
at a different core version. Given verify/engine.rs's
const TAMPER: [&str; 3] = ["hashMismatch", "cycle", "malformedRef"], that
surfaces as component tree integrity violation — a tamper finding caused by a
version difference. This one is not optional.

crates/dpp-types/src/evidence.rs + crates/dpp-vault/src/domain/service/evidence.rs
component_graph is Option<serde_json::Value> attested via content_hash, so
the type does not break but the hashed content shape changes. Worth deciding
explicitly what that means for dossiers assembled before and after.

crates/dpp-types/src/passport_request.rs + crates/dpp-vault/src/handlers/create.rs
parentPassportRef is a create-API field with its own shape validation
(validate_passport_ref(parent, "parentPassportRef")). New field name, new
shape, and operation is required, so this is an API contract change with a
validation rule attached.

crates/dpp-vault/src/api/passport_response.rs — served shape.

crates/dpp-vault/src/domain/verify/reference.rs — second-life verification
path; check whether the plural edge changes anything for it.

crates/dpp-dal/src/pg/repo_passport.rs — good news: it derives its
protected list from core's via ADDED_HERE/REMOVED_HERE, and
REMOVED_HERE = ["componentRefs"] still matches since that key name is
unchanged. derivedFrom is picked up automatically. Only the doc comments
naming parentPassportRef go stale.

OpenAPI

api/components/schemas/{CreateRequest,PassportResponse}.yaml, plus
api/openapi.bundled.{json,yaml}. The contract gate fails until regenerated.

The item worth doing first, and separately

crates/dpp-resolver/src/handlers/resolve_by_gtin.rs builds the public GS1
linkset by hand-navigating raw JSON, and it has no tests.

passport.get("parentPassportRef").and_then(|r| r.get("uri"))   // → Predecessor
passport.get("componentRefs").filter_map(|c| c.get("uri"))  // → hasComponent

After the change, the first is None and the second drops every entry. So the
Predecessor and hasComponent relations simply stop being emitted — a QR scan
can no longer walk to a battery's predecessor or into its bill of materials.

Three things make this the priority:

  1. Core's tolerant reader does not help here. This code never deserializes
    into ComponentRef; it reads entry["uri"] off a Value.
  2. It fails silently by constructionand_thenNone, filter_map
    drop. No error, no log, no degraded response. The linkset is emitted without
    the relations.
  3. It is untested. Gs1LinkType::Predecessor and HasComponent appear at
    exactly two places in this repository, both in that source file. The file has
    three inline tests and neither symbol appears in any of them.

Suggested order: write the characterization tests against main first, before
the core change lands. Tests asserting that a passport with lineage emits both
relations, written against today's behaviour, turn this from a silent regression
into a failing test the moment the shape changes. Writing them afterwards means
relying on someone remembering the relations existed.

Verification caveat for the repin

The committed Cargo.lock has no source or checksum for the dpp-*
packages, so it was generated with .cargo/config.toml's [patch.crates-io]
override active, pointing at a local dpp-core tree. That file's own header
notes CI and release builds intentionally have no such config.

So verifying the repin against the published crates requires switching the
override off first (just core-published) — otherwise the build is against a
working tree and proves nothing about the released version.

Not in scope

dpp-web does not reference these shapes. The CLI's client-side validation
heuristic names only productName and productGroupData, so it is unaffected.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedCannot proceed until a named dependency resolves: an EU act, an upstream release, or a decisionenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions