Skip to content

feat(sdk): load TestJam state from JIP-4 genesis.json - #133

Merged
tomusdrw merged 6 commits into
mainfrom
td-issue-104
May 17, 2026
Merged

feat(sdk): load TestJam state from JIP-4 genesis.json#133
tomusdrw merged 6 commits into
mainfrom
td-issue-104

Conversation

@tomusdrw

Copy link
Copy Markdown
Member

Summary

  • Add loadStateFromGenesis(genesis) SDK primitive that materialises a SerializedState<StateEntries> from a parsed JIP-4 JipChainSpec — the inverse of generateGenesis, and a thin wrapper over typeberry's state_merkleization.loadState.
  • Route TestJam.create() and TestJam.empty() through the new primitive. TestJam.state now holds SerializedState<StateEntries> at runtime — the same state shape typeberry consumes when it loads genesis.json at deploy time, instead of the hand-rolled InMemoryState that mirrored JAM service-account semantics in generateState. Any drift between SDK construction and typeberry's deserialiser now surfaces in tests, not at jammin deploy && jammin start.
  • New factory TestJam.fromGenesis(path?: string) reads a JIP-4 file from disk, defaults to ./dist/genesis.json so it "just works" after a jammin deploy. Missing files throw with a clear hint to run jammin deploy first; malformed JSON / schema errors propagate unmodified from JSON.parse / parseFromJson.

Closes #104.

Behaviour change to flag

TestJam.state previously held InMemoryState; it now holds SerializedState<StateEntries> for instances built via create() and empty() (and the new fromGenesis()). The declared type union is unchanged. accumulate() already branched on both shapes, so no consumer changes are required unless code was narrowing jam.state instanceof InMemoryState directly.

Spec & plan

  • Design: docs/superpowers/specs/2026-05-17-load-sdk-state-from-genesis-design.md (gitignored — workflow artefact only)
  • Plan: docs/superpowers/plans/2026-05-17-load-sdk-state-from-genesis.md (gitignored — workflow artefact only)

Test plan

  • bun test packages/jammin-sdk — 163 SDK tests pass (4 new round-trip tests for loadStateFromGenesis; 1 state-shape test for TestJam.empty(); 3 new tests for TestJam.fromGenesis() covering happy path, missing-file error, and an accumulate() regression).
  • bun test — full suite (SDK + CLI), 193 pass / 0 fail / 1 skip across 14 files.
  • bun run build — clean build, no TS errors.
  • bun run qa — Biome clean.
  • Public-surface smoke check: loadStateFromGenesis, TestJam.fromGenesis, TestJam.empty, TestJam.create all reachable through packages/jammin-sdk/dist/index.js.

Out of scope

  • Changing the on-disk JIP-4 schema or the jammin deploy flow.
  • Touching jammin start or the Docker-based local network.
  • Removing or rewriting generateState / generateGenesis — they remain the way jammin deploy produces the file in the first place.
  • Schema validation of the loaded genesis.json beyond what JipChainSpec.fromJson already does.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74086565-61db-4ee6-bf36-265d97544e32

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements genesis-driven SDK state loading by introducing a new loadStateFromGenesis helper and refactoring TestJam to load state from JIP-4 genesis files instead of generating it in-memory. State round-tripping, file validation, and error handling are comprehensively tested.

Changes

SDK State Loading from Genesis

Layer / File(s) Summary
loadStateFromGenesis implementation and validation
packages/jammin-sdk/utils/genesis-state-generator.ts, packages/jammin-sdk/utils/genesis-state-generator.test.ts
New exported helper loadStateFromGenesis reconstructs SerializedState from JIP-4 genesis entries. Tests verify round-tripping of service code, storage, preimages, and empty genesis handling.
TestJam refactoring to genesis-driven initialization
packages/jammin-sdk/simulator.ts, packages/jammin-sdk/simulator.test.ts
TestJam.create() and TestJam.empty() now use generateGenesis() + loadStateFromGenesis(). TestJam.fromGenesis(path) reads/parses a genesis.json file with validation and error handling. Tests validate loaded state types, service retrieval, file-not-found errors, and accumulate() behavior with genesis-loaded state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • FluffyLabs/jammin#106: Directly refines the same TestJam implementation introduced by PR #106, adding genesis-driven initialization via the new loadStateFromGenesis helper.
  • FluffyLabs/jammin#86: Related to the introduction of SDK accumulation simulator and genesis utilities that this PR now leverages for state loading.
  • FluffyLabs/jammin#110: Both PRs update genesis-state-generator.ts to handle JIP-4 genesis_state representation and consumption, with this PR adding the state loader to consume the schema.

Suggested reviewers

  • DrEverr
  • mateuszsikora
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding the ability to load TestJam state from a JIP-4 genesis.json file, which is the core objective of the PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the new loadStateFromGenesis primitive, TestJam refactoring, and the new fromGenesis factory method.
Linked Issues check ✅ Passed The PR fully addresses issue #104 by implementing loadStateFromGenesis primitive and TestJam.fromGenesis factory, enabling SDK to load state directly from JIP-4 genesis.json files.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked objective (#104): adding genesis loading capability to the SDK. The PR remains focused on the stated scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-issue-104

Comment @coderabbitai help to get the list of available commands and usage tips.

Wraps typeberry's state_merkleization.loadState to materialise a
SerializedState<StateEntries> from a parsed JIP-4 chain spec. This is
the inverse of generateGenesis and gives the SDK an explicit,
public-facing "load state" API to complement the existing
"generate / save" pair.

Refs #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/jammin-sdk/simulator.test.ts`:
- Around line 176-180: The code uses String.prototype.substring(2) in the
genesis payload construction which violates the Biome noSubstr rule; update all
uses to String.prototype.slice(2) instead. Specifically, replace
genesis.genesisHeader.toString().substring(2) with
genesis.genesisHeader.toString().slice(2), and in the genesis_state mapping
replace key.toString().substring(2) and value.toString().substring(2) with
key.toString().slice(2) and value.toString().slice(2) respectively so linting
passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: df57859c-ccfd-4d7a-98a9-907e092df167

📥 Commits

Reviewing files that changed from the base of the PR and between 4b377ec and bcbb5b3.

📒 Files selected for processing (4)
  • packages/jammin-sdk/simulator.test.ts
  • packages/jammin-sdk/simulator.ts
  • packages/jammin-sdk/utils/genesis-state-generator.test.ts
  • packages/jammin-sdk/utils/genesis-state-generator.ts

Comment thread packages/jammin-sdk/simulator.test.ts Outdated
tomusdrw and others added 4 commits May 17, 2026 10:40
TestJam now holds SerializedState<StateEntries> at runtime — the same
shape typeberry consumes when it loads genesis.json at deploy-time. The
factories build state via generateGenesis + loadStateFromGenesis
instead of constructing an InMemoryState directly, so any drift between
the SDK's hand-rolled state construction and typeberry's deserialiser
is caught by tests rather than only surfacing at deploy time.

Refs #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New static factory loads SerializedState from a JIP-4 genesis.json
file on disk. Default path is ./dist/genesis.json so it 'just works'
after a jammin deploy. Missing files throw with a clear hint to run
deploy first; malformed JSON / schema errors propagate unmodified from
the JSON parser and typeberry's parseFromJson.

Closes #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sanity check that accumulate() runs against state loaded from a file,
exercising the SerializedState branch end-to-end through the new
fromGenesis factory.

Refs #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TypeScript's strict generic inference for parseFromJson<T> couldn't
narrow T from JipChainSpec.fromJson alone (FromJsonWithParser<unknown,
JipChainSpec> is one of several union members of FromJson<T>, so
inference picks unknown). Pin T explicitly.

Refs #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Biome's noSubstr rule prefers slice over substring. Flagged by
CodeRabbit on PR #133.

Refs #104

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tomusdrw
tomusdrw merged commit 270ae68 into main May 17, 2026
8 checks passed
@tomusdrw
tomusdrw deleted the td-issue-104 branch May 17, 2026 19:29
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.

Load SDK state from genesis.json file

1 participant