feat: forward chain_metadata and expose digests in HTTP gateway#287
Conversation
The gateway was hardcoding chain_metadata: None in ParseRequest and
only returning signable_payload from the response. This wires up the
full round-trip:
- TurnkeyRequest now accepts chain_metadata (optional), using a local
ChainMetadataInput tagged enum to avoid untagged-serde ambiguity
between Ethereum/Solana (both have networkId)
- ParseRequest forwards chain_metadata to the gRPC backend
- TurnkeyPayload now includes metadata_digest and input_payload_digest
from the backend response
- error_response uses the spec-mandated empty-SHA256 sentinel for
digest fields instead of empty string
Also fixes the serde_derive feature in the generated crate: the
previous blanket .type_attribute(".parser", ...) applied serde to
QosParserRequest/QosParserResponse which embed health/rpc types that
don't implement serde. Replaced with explicit per-type attributes,
excluding the QOS types. Removed orphaned parser.v1.rs (not included
in _include.rs and never compiled).
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the HTTP gateway to correctly forward chain_metadata into gRPC parse requests and to return metadataDigest / inputPayloadDigest in HTTP responses, while also fixing the generated crate’s serde_derive feature by scoping serde attributes to only compatible parser types.
Changes:
- Gateway: forward
chain_metadatatoParseRequest, addmetadataDigestandinputPayloadDigestto the HTTP response payload, and use an empty-SHA256 sentinel in error responses. - Codegen / generated outputs: enable and repair
serde_deriveby applying serde only to selected.parsertypes and scopingserde(untagged)toChainMetadata’s oneof. - Cleanup: remove an orphaned, uncompiled generated file (
parser.v1.rs).
Reviewed changes
Copilot reviewed 3 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/parser/gateway/src/main.rs |
Forwards chain_metadata, exposes digest fields in HTTP responses, and adds a tagged JSON input wrapper for chain metadata. |
src/parser/gateway/Cargo.toml |
Enables generated/serde_derive for the gateway. |
src/codegen/src/main.rs |
Narrows serde application to specific parser types; scopes serde(untagged) to the ChainMetadata oneof only. |
src/generated/src/generated/parser.rs |
Regenerated output reflecting scoped serde derives and the corrected oneof untagged behavior. |
src/generated/src/generated/grpc.health.v1.rs |
Removes unintended serde(untagged) attribute from health enums. |
src/generated/src/generated/google.rpc.rs |
Removes unintended serde(untagged) attribute from google RPC enums. |
src/generated/src/generated/parser.v1.rs |
Deletes orphaned generated file that was not included/compiled. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
EthereumMetadata.abi_mappings and SolanaMetadata.idl_mappings are HashMap fields without serde(default), so JSON input that omits them fails deserialization. Add serde(default) via codegen field_attribute so callers can omit empty maps without errors. Co-Authored-By: Claude <noreply@anthropic.com>
The doc comment said network_id but SolanaMetadata uses serde(rename_all = "camelCase"), so the actual JSON field name clients send is networkId. Co-Authored-By: Claude <noreply@anthropic.com>
…serialization Covers the three behaviors changed in recent commits: - error_response always sets EMPTY_SHA256 in both digest fields - ChainMetadataInput tagged enum prevents Solana being misread as Ethereum - serde(default) on abi_mappings/idl_mappings allows omitting empty maps Co-Authored-By: Claude <noreply@anthropic.com>
r-n-o
left a comment
There was a problem hiding this comment.
I'm not super familiar with the parser/gateway crate. Is that something you use internally to test visualsign? In a normal setting I don't think you should need it because Turnkey already runs an HTTP gateway (the Turnkey gateway powers the 2 paths for dev and prod, /visualsign-dev/api/v2/parse and /visualsign/api/v2/parse). If the bug is also present in the Turnkey gateway I'll have to make a change there.
So I believe this change will be a no-op from the point of view of the visualsign parser app? (and the dev+prod endpoints)
Regardless: I see you have new tests in main.rs so this is clearly fixing something 😅
I was testing this PR and noticed that we were ignoring the |
prasanna-anchorage
left a comment
There was a problem hiding this comment.
Approving. The discriminated-union ChainMetadataInput wrapper is a clean fix for the serde(untagged) ambiguity that would silently deserialize a Solana-only payload (just networkId) as EthereumMetadata. Codegen narrowing from crate-wide SERDE_DERIVE to per-type is the right call given QOSParserRequest/Response embed health/google.rpc types without serde. Empty-SHA256 sentinel in error_response matches the spec. Test coverage hits all four key behaviors. Mixed snake-outer / camel-inner JSON shape is preserved for back-compat with explicit documentation.
r-n-o's clarifying question is resolved (Turnkey production gateway has the same bug — separate ticket on their side, not blocking this fix in the local-dev gateway).
Why am I making this PR?
The HTTP gateway was dropping
chain_metadatafrom parse requests and not returningmetadata_digest/input_payload_digestin responses. Clients sending ABI/IDL context got no benefit from it, and the digests needed for signature verification were never surfaced.What am I changing?
chain_metadatathrough to the gRPC backendmetadataDigestandinputPayloadDigestinTurnkeyPayloadabiMappings,idlMappings) can now be omitted from JSON requestsWhat is the Linear ticket?
N/A
What are the rollback steps?
Revert this PR. No database or config changes.
Is this change backwards compatible?
Yes for requests.
chain_metadatais optional so existing clients continue to work.For responses:
metadataDigestandinputPayloadDigestare new fields. Clients with lenient JSON parsing are unaffected; strict schema validation will see two new fields.Does this require cross-team/service coordination?
No. The gRPC backend already supports these fields.
How do I know it works as designed? Which tests exercise this code?
Co-authored with Claude.