Skip to content

feat(node): add gear_readWasmCustomSection RPC endpoint#5275

Open
ukint-vs wants to merge 8 commits intomasterfrom
vs/read-wasm-custom-section
Open

feat(node): add gear_readWasmCustomSection RPC endpoint#5275
ukint-vs wants to merge 8 commits intomasterfrom
vs/read-wasm-custom-section

Conversation

@ukint-vs
Copy link
Copy Markdown
Member

@ukint-vs ukint-vs commented Apr 2, 2026

Summary

  • Add node-level RPC gear_readWasmCustomSection(code_id, section_name, at?) that reads custom sections from program WASM original code stored on-chain
  • Add get_custom_section_data() utility in gear-core with 5 unit tests
  • Primary use case: clients retrieve Sails IDL v2 embedded in the sails:idl WASM section without needing original build artifacts

Test plan

  • Unit tests for get_custom_section_data (5 cases: found, not found, duplicates, invalid WASM, empty input)
  • Manual test: upload a sails program with embedded IDL, call gear_readWasmCustomSection via RPC
  • Verify historical block hash queries work via at parameter

🤖 Generated with Claude Code

Add a node-level RPC that reads custom sections from program WASM
original code stored on-chain. This enables clients to retrieve
Sails IDL v2 (or any named custom section) without original build
artifacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@semanticdiff-com
Copy link
Copy Markdown

semanticdiff-com Bot commented Apr 2, 2026

Review changes with  SemanticDiff

Changed Files
File Status
  core/src/code/mod.rs  7% smaller
  Cargo.lock Unsupported file format
  core/Cargo.toml Unsupported file format
  core/src/code/utils.rs  0% smaller
  gsdk/src/rpc.rs  0% smaller
  gsdk/tests/rpc.rs  0% smaller
  node/service/src/rpc/mod.rs  0% smaller
  node/service/src/rpc/wasm_section.rs  0% smaller

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new node-level RPC endpoint designed to improve developer experience by allowing clients to fetch custom sections from on-chain WASM programs. This is particularly useful for retrieving embedded metadata, such as Sails IDL, without requiring external build artifacts. The implementation includes robust utility functions for WASM parsing and ensures compatibility with historical block queries.

Highlights

  • New RPC Endpoint: Added the gear_readWasmCustomSection RPC endpoint to allow clients to retrieve custom sections from on-chain WASM code.
  • Utility Implementation: Implemented get_custom_section_data in gear-core to safely extract specific WASM sections, supported by comprehensive unit tests.
  • Dependency Update: Added wasm-encoder to the project dependencies to facilitate WASM manipulation in tests.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new RPC endpoint gear_readWasmCustomSection to allow clients to retrieve custom sections from original WASM code stored on-chain, which is particularly useful for extracting Sails IDL data. It includes the necessary logic in gear-core to parse WASM binaries for specific sections and adds the RPC implementation in the node service. A critical issue was identified regarding the hardcoded pallet name GearProgram used for storage key construction, which likely conflicts with the actual pallet name Gear used in the runtime, potentially causing the RPC to always return null. Additionally, the manual construction of storage keys using the Identity hasher is noted as a fragile implementation detail.

Comment thread node/service/src/rpc/wasm_section.rs Outdated
Comment thread node/service/src/rpc/wasm_section.rs Outdated
Comment thread node/service/src/rpc/wasm_section.rs Outdated
Comment thread node/service/src/rpc/wasm_section.rs Outdated
Comment thread core/src/code/utils.rs Outdated
ukint-vs and others added 4 commits April 2, 2026 19:21
Move inline crate paths to proper `use` imports for
`gear_core::code::get_custom_section_data` and
`parity_scale_codec::Decode`.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@ukint-vs
Copy link
Copy Markdown
Member Author

@claude opus review

@claude
Copy link
Copy Markdown

claude Bot commented Apr 23, 2026

Claude finished @ukint-vs's task in 3m 38s —— View job


Claude Opus Review

The PR adds a node-service-level RPC gear_readWasmCustomSection that reads the on-chain original WASM for a given code_id, parses out a named custom section, and returns its raw bytes. The storage-prefix construction mirrors the existing pattern in pallets/gear/rpc/src/lib.rs:188, and the parsing logic is split into a reusable helper in gear-core. Overall the change is self-contained, read-only, introduces no runtime/consensus behavior, and is a reasonable way to expose embedded metadata (notably sails:idl) without a runtime API bump.

Findings below.


severity: mediumsaw_version heuristic re-implements logic that wasmparser already provides, and the &'static str error throws away useful diagnostics.

File: core/src/code/utils.rs lines 545–568

pub fn get_custom_section_data<'a>(
    wasm: &'a [u8],
    section_name: &str,
) -> Result<Option<&'a [u8]>, &'static str> {
    let parser = wasmparser::Parser::new(0);
    let mut saw_version = false;

    for payload in parser.parse_all(wasm) {
        let payload = payload.map_err(|_| "invalid wasm binary")?;
        match payload {
            Payload::Version { .. } => saw_version = true,
            Payload::CustomSection(section) if section.name() == section_name => {
                return Ok(Some(section.data()));
            }
            _ => {}
        }
    }

    if !saw_version {
        return Err("invalid wasm binary");
    }

    Ok(None)
}

Two issues bundled:

  1. wasmparser already exposes a sniff/validation shortcut (this is what @StackOverflowExcept1on pointed to). Checking the magic + version up front is cleaner than threading a saw_version boolean through the payload loop — that pattern mixes "is this a wasm file at all?" with "is the section present?" concerns.
  2. The &'static str error discards the underlying BinaryReaderError detail that .map_err(|_| ...) throws away. Since the only caller surfaces this string as RPC error data, losing the parser's offset/reason makes on-chain corruption (in the unlikely event) much harder to diagnose. Returning CodeError::Validation(_) or at least a String carrying format!("{err}") would preserve the information without any callsite change.

Fix this →


severity: medium — verification gap: no end-to-end test for the RPC.

The 5 unit tests cover get_custom_section_data on synthetic WASM, which is useful but tests only the helper. Things not exercised by any automated test:

  • Storage key construction (prefix + Identity(code_id)) — a silent mismatch with the pallet's storage definition would make the RPC always return null. That's exactly the failure mode @gemini-code-assist worried about; the only thing protecting us today is manual inspection.
  • SCALE-decoding of the stored Vec<u8> envelope in wasm_section.rs:100.
  • Historical block query via at — flagged as unchecked in the PR's own test plan.
  • Return shape over the wire (Option<Bytes> vs null).

Given the PR's test plan has two items unchecked, I'd suggest at minimum a gsdk/integration test that uploads a code blob with a known custom section and round-trips it via the RPC. This would catch any future pallet storage rename or hasher change immediately, rather than at the next manual smoke test.


severity: low — tests use fully-qualified wasm_encoder::* paths (also flagged by @StackOverflowExcept1on).

File: core/src/code/mod.rs lines 1253–1309

let mut module = wasm_encoder::Module::new();
let mut types = wasm_encoder::TypeSection::new();
types.ty().function(vec![], vec![]);
module.section(&types);
module.section(&wasm_encoder::CustomSection {
    name: name.into(),
    data: data.into(),
});

Per rustfmt.toml imports_granularity = "Crate" and general convention in this repo, add use wasm_encoder::{Module, TypeSection, CustomSection}; at the top of the test module and drop the wasm_encoder:: prefixes.


severity: low — minor: first_match_returned test duplicates the body of make_wasm_with_custom_section.

File: core/src/code/mod.rs lines 1279–1296

The helper make_wasm_with_custom_section is inlined again because the test needs two custom sections. Either extend the helper to accept a slice of (name, data) pairs, or at minimum consolidate after the import cleanup above — the current duplication will drift if the skeleton module changes.


Notes / non-findings

  • Storage prefix fragility (raised by @gemini-code-assist, responded to by author): the GearProgram prefix is correct and matches existing usage. The Identity hasher coupling on CodeId is inherent to how OriginalCodeStorage is defined (pallets/gear-program/src/lib.rs:231) and the same pattern is used by the existing Gear RPC; acceptable as-is.
  • Error code 9000 matches the convention used by node/service/src/rpc/runtime_info.rs:99 for other node-service RPCs — consistent.
  • wasm-encoder is added as [dev-dependencies] — good, it does not ship in runtime builds.
  • No consensus / storage / runtime-API impact. This is purely a read-only node-service RPC.

Summary

Overall the change is sound and follows existing conventions. The most meaningful improvement would be an integration test that exercises the full RPC path, because the helper-level unit tests can't catch a storage-key or decoding regression. Secondary: clean up the saw_version heuristic and preserve parser error detail.
· vs/read-wasm-custom-section

- Drop the `saw_version` flag in `get_custom_section_data` — `parse_all`
  already rejects input without a valid preamble, so the flag was
  redundant. Return `wasmparser::BinaryReaderError` instead of
  `&'static str` so parser offset + reason propagate through the RPC.
- Clean `wasm_encoder::*` prefixes out of the test module by importing
  `Module`, `TypeSection`, `CustomSection` at the top.
- Consolidate the test helper into
  `make_wasm_with_custom_sections(&[(&str, &[u8])])` so
  `first_match_returned` shares it instead of inlining the module body.
- Add `Api::read_wasm_custom_section[_at]` in gsdk and a gsdk
  integration test that round-trips a `@custom "sails:idl"` WAT via
  `upload_code` + the RPC, covering the present, missing-section, and
  unknown-`code_id` cases. This exercises the full storage-key path
  (`twox_128("GearProgram") ++ twox_128("OriginalCodeStorage") ++ code_id`)
  and the SCALE envelope decode.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ukint-vs
Copy link
Copy Markdown
Member Author

/review

@ukint-vs ukint-vs self-assigned this Apr 23, 2026
@ukint-vs ukint-vs added A0-pleasereview PR is ready to be reviewed by the team C1-feature Feature request labels Apr 23, 2026
ukint-vs and others added 2 commits April 27, 2026 00:31
Apply review feedback from /simplify:

- node/service/src/rpc/wasm_section.rs:
  - Collapse PhantomData<Block> + PhantomData<Backend> into a tuple field
  - Drop cached `original_code_prefix` field; compute inline with a
    `keep in sync with pallet-gear-program::OriginalCodeStorage` note
  - Inline single-use `map_err_into_rpc_err` helper; promote 9000 to
    `ERROR_CODE` const
  - Standardize error formatting on `Display` (was a mix of `{:?}` and
    `to_string()`) so RPC errors don't leak struct internals
  - Avoid SCALE double-allocation by decoding the `Compact<u32>` length
    prefix and slicing into the storage buffer directly
  - Delete WHAT-narration comments

- core/src/code/utils.rs: drop multi-line comment defending an
  implementation detail already covered by the doc comment

- gsdk/tests/rpc.rs: drop WHAT-narration comments in
  test_read_wasm_custom_section

- gsdk/src/rpc.rs: clarify in the doc that `block_hash: None` falls
  back to the best block (preserving the trailing `at specified block`
  required by the `#[at_block]` proc macro)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Comment on lines +81 to +84
// Keep in sync with `pallet-gear-program::OriginalCodeStorage` (StorageMap, Identity hasher).
let mut storage_key = twox_128(b"GearProgram").to_vec();
storage_key.extend_from_slice(&twox_128(b"OriginalCodeStorage"));
storage_key.extend_from_slice(code_id.as_bytes());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move impl inside pallet gear, to use normally CodeStorage instead of this raw code

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

Labels

A0-pleasereview PR is ready to be reviewed by the team C1-feature Feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants