Skip to content

Adapt to wire-sysio table_id namespace isolation; long table name support#12

Merged
heifner merged 3 commits into
masterfrom
feature/wire-sysio-table-id
Apr 20, 2026
Merged

Adapt to wire-sysio table_id namespace isolation; long table name support#12
heifner merged 3 commits into
masterfrom
feature/wire-sysio-table-id

Conversation

@heifner

@heifner heifner commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Wire-sysio PR Wire-Network/wire-sysio#288 widened table_def.name from sysio::name (uint64) to free-form std::string and added table_id (uint16, DJB2 hash of the table name % 65536) and secondary_indexes for KV-table per-table namespace isolation. The binary wire format break means abieos must be updated in lockstep with the chain side or every field after tables in abi_def parses misaligned, and any contract whose table name is longer than 12 chars is unreachable through the C API.

This PR takes the chance to make the long-table-name story end-to-end by switching the internal table_types map and the C API entry point to free-form strings.

Companion PRs:

⚠️ Breaking C API change

abieos_get_type_for_table now takes the table name as a const char* instead of a uint64-encoded sysio name:

// Before
const char* abieos_get_type_for_table(abieos_context*, uint64_t contract, uint64_t table);
// After
const char* abieos_get_type_for_table(abieos_context*, uint64_t contract, const char* table);

The only known consumer of this entry point in the Wire ecosystem is node-abieos (the native binding used by Wire-Network/hyperion-history-api), which was already converting JS strings to uint64 via abieos_string_to_name() before calling this function — that round trip is now redundant and node-abieos can pass the string straight through. node-abieos will be updated in a follow-on PR.

Changes

include/sysio/abi.hpp

  • New index_def struct: {name: string, key_type: string, table_id: uint16} mirroring sysio::chain::index_def
  • table_def: name: name → string, +table_id: uint16, +secondary_indexes: index_def[]
  • abi_def: +protobuf_types as a might_not_exist<std::string> extension to mirror wire-sysio's reflection layout (variants, action_results, enums, protobuf_types). Keeps binary forward-compat for ABIs that include the field even though abieos itself doesn't consume it.
  • abi.table_types: std::map<sysio::name, string>std::map<std::string, std::string, std::less<>>. The std::less<> transparent comparator lets callers do find(string_view) without constructing a temporary std::string per lookup.
  • abi.abi_types likewise gets std::less<>.
  • New aliases: table_type_map, abi_type_map (used in abi.cpp signatures).
  • to_json(abi_def) writes protobuf_types unconditionally for consistency with the surrounding extension fields (variants, action_results, enums) which always emit even when empty.

src/abieos.hpp

  • jobject typedef gets std::less<> for the same transparent-lookup reason — the JSON object map is the hottest map in abieos.

src/abi.cpp

  • convert(abi_def, abi) populates table_types directly with the new string-typed table_def.name; no name conversion needed.
  • Function signatures updated to use abi_type_map alias.

src/abieos.h, src/abieos.cpp

  • abieos_get_type_for_table signature change as described above.
  • Internal lookup uses std::string_view via the std::less<> transparent comparator to avoid an allocation per call.

src/test.cpp

  • tokenHexAbi regenerated for the new binary format. The token ABI shape (sysio::abi/1.0) is unchanged, but the on-wire encoding of tables[].name is now a length-prefixed string instead of a uint64. The new hex was produced from the canonical token ABI JSON via abieos_abi_json_to_bin().

external/wirejs-native

Tests

All four C++ tests pass against the updated layout:

  • test_abieos
  • test_abieos_template
  • test_abieos_key
  • test_abieos_reflect

…port

Wire-sysio PR Wire-Network/wire-sysio#288 widened table_def.name from
sysio::name (uint64) to free-form std::string and added table_id
(uint16, DJB2 hash of the table name % 65536) and secondary_indexes
for KV-table per-table namespace isolation. The binary wire format
break means abieos must be updated in lockstep with the chain side or
every field after `tables` in abi_def parses misaligned, and any
contract whose table name is longer than 12 chars is unreachable
through the C API.

This commit takes the chance to make the long-table-name story
end-to-end by switching the internal table_types map and the C API
entry point to free-form strings.

include/sysio/abi.hpp:
 - new index_def struct (name: string, key_type: string, table_id: uint16)
 - table_def: name → string; +table_id (uint16); +secondary_indexes
 - abi_def: +protobuf_types as a might_not_exist<std::string> extension
   to mirror wire-sysio's reflection layout (variants, action_results,
   enums, protobuf_types). Keeps binary forward-compat for ABIs that
   include the field even though abieos itself doesn't consume it.
 - abi.table_types: std::map<sysio::name, string> →
   std::map<std::string, std::string, std::less<>>. The std::less<>
   transparent comparator lets callers do find(string_view) without
   constructing a temporary std::string per lookup.
 - abi.abi_types likewise gets std::less<>.
 - new aliases: table_type_map, abi_type_map (used in abi.cpp signatures).
 - to_json(abi_def) writes protobuf_types unconditionally for
   consistency with the surrounding extension fields (variants,
   action_results, enums) which always emit even when empty.

src/abieos.hpp:
 - jobject typedef gets std::less<> for the same transparent-lookup
   reason — the JSON object map is the hottest map in abieos.

src/abi.cpp:
 - convert(abi_def, abi) populates table_types directly with the new
   string-typed table_def.name; no name conversion needed.
 - Function signatures updated to use abi_type_map alias.

src/abieos.h, src/abieos.cpp — BREAKING C API change:
 - abieos_get_type_for_table(context, uint64_t contract, const char* table)
   replaces the old uint64_t table parameter. The C API now accepts free-
   form table names directly so long names work end-to-end. Internal
   lookup uses std::string_view via the std::less<> transparent
   comparator to avoid an allocation per call.
 - The only known consumer of this entry point in the Wire ecosystem is
   node-abieos (Wire-Network/hyperion-history-api's native binding),
   which was already converting JS strings to uint64 via
   abieos_string_to_name() before calling this function — that round
   trip is now redundant and node-abieos can pass the string straight
   through. node-abieos will be updated separately.

src/test.cpp:
 - tokenHexAbi regenerated for the new binary format. The token ABI
   shape (sysio::abi/1.0) is unchanged, but the on-wire encoding of
   tables[].name is now a length-prefixed string instead of a uint64.
   The new hex was produced from the canonical token ABI JSON via
   abieos_abi_json_to_bin().

external/wirejs-native bumped to feature/wire-sysio-table-id with the
companion TS schema changes (Wire-Network/wirejs-native#4). All four
abieos C++ tests (test_abieos, test_abieos_template, test_abieos_key,
test_abieos_reflect) pass against the updated layout.

Companion PRs:
 - Wire-Network/wire-sysio#288 (chain-side table_id namespace isolation)
 - Wire-Network/wirejs-native#4 (TS schema mirror of this commit)
heifner added a commit to Wire-Network/node-abieos that referenced this pull request Apr 10, 2026
…rings

Wire-sysio PR Wire-Network/wire-sysio#288 widened table_def.name from
sysio::name (uint64) to free-form std::string and added table_id
(uint16) + secondary_indexes for KV-table per-table namespace
isolation. The companion abieos PR Wire-Network/abieos#12 changed the
C API entry point abieos_get_type_for_table() to take a const char*
table name instead of a uint64 sysio name.

This commit picks up the new abieos and updates the binding to match:

- abieos submodule bumped to feature/wire-sysio-table-id (commit
  9c1000a, the head of Wire-Network/abieos#12). The bump pulls in
  table_def long-name support, the breaking C API signature change,
  the abi.table_types map keyed by std::string with std::less<>
  transparent comparator, and the regenerated tokenHexAbi test
  fixture for the new on-wire encoding.

- src/main.cpp:182 get_type_for_table() — drop the table-name string
  → uint64 round trip via abieos_string_to_name() that the wrapper
  used to do before calling abieos_get_type_for_table(). The round
  trip was lossy: any table name longer than 12 characters or
  containing characters outside the sysio name alphabet was silently
  truncated, making the table type unreachable. The binding now
  passes the JS string straight through. The contract name still
  goes through abieos_string_to_name() because contract identifiers
  ARE sysio::name on both sides of the API.

- examples/ABIs/eosio.token.raw regenerated against the new abieos
  binary table_def encoding. The fixture is base64-encoded binary
  ABI; the old fixture used the legacy table_def shape (uint64 name)
  and produced "Stream overrun" on load against the new abieos. The
  contained ABI shape (eosio::abi/1.1, two tables: accounts/stat) is
  unchanged — only the encoding of tables[].name changes from
  "fixed 8 bytes" to "varint length + bytes".

- lib/abieos.node rebuilt against the new abieos with clang++-18 and
  cmake-js compile --CDSKIP_SUBMODULE_CHECK=1 (the parent submodule
  ref is intentionally ahead of master while abieos PR #12 is in
  flight).

examples/basic.mjs now passes 6/6 positive checks (eosio voteproducer,
eosio.token transfer + stat, eosio.msig approvals2 + proposal,
eosio producers); the seventh check is a deliberate negative test
that looks up a non-existent table on a non-existent contract.

Companion PRs:
 - Wire-Network/wire-sysio#288 — chain-side table_id namespace isolation
 - Wire-Network/abieos#12 — abieos C API + binary format update
 - Wire-Network/wirejs-native#4 — TS schema mirror
heifner added a commit to Wire-Network/wire-libraries-ts that referenced this pull request Apr 10, 2026
Wire-sysio PR Wire-Network/wire-sysio#288 widened table_def.name from
sysio::name (uint64) to a free-form std::string and appended two new
fields — table_id (uint16, DJB2 hash of the table name % 65536) and
secondary_indexes (vector<index_def>) — for KV-table per-table
namespace isolation. The wire format break means any sdk-core caller
that loads a contract ABI from on-chain via get_raw_abi (or any other
binary-encoded source) parses misaligned starting at the first table
field — name reads the wrong bytes, every subsequent field is shifted,
and ricardian_clauses ends up reading garbage.

PR Wire-Network/wire-sysio#290 separately changed the get_table_rows
HTTP response shape for KV-backed tables. Each row used to be the
decoded struct directly (or {data, payer} when show_payer was set);
the unified endpoint now returns {key, value, payer?} per row. The
ChainAPI wrapper used to destructure {data, payer} on show_payer and
then map rows through Serializer.decode for typed callers — both paths
break against the new shape.

This commit takes a hard break on the binary format (no fallback to
the legacy EOSIO 8-byte name encoding) and a backward-compatible
unwrap on the HTTP response shape.

packages/sdk-core/src/chain/Abi.ts:
 - new ABI.Index interface (name, key_type, table_id) mirroring
   sysio::chain::index_def in wire-sysio's
   libraries/chain/include/sysio/chain/abi_def.hpp.
 - ABI.Table gains optional table_id (uint16) and secondary_indexes
   (Index[]) fields. Optional so hand-built test fixtures and ABIs
   constructed programmatically don't need to specify them; the
   encoder defaults to 0 / [] when omitted.
 - fromABI() reads name as a length-prefixed string instead of an
   8-byte sysio::name uint64, then consumes table_id (uint16 LE) and
   secondary_indexes (vector<index_def>) after the existing type
   field. Also reads a trailing protobuf_types extension after enums
   so that any subsequent extension appended to abi_def parses
   cleanly to EOF.
 - toABI() mirrors the parser: writes table_def.name via
   encoder.writeString(String(table.name)) (the String() coercion
   keeps NameType-typed callers compiling), then table_id and
   secondary_indexes, and finally the empty protobuf_types string.

packages/sdk-core/src/api/v1/Chain.ts:
 - get_table_rows() now detects the unified KV row shape — each row
   has both `key` and `value` keys — and unwraps to the inner
   value before downstream processing. The old EOSIO shapes
   (decoded struct directly, or {data, payer} on show_payer) are
   left untouched, so sdk-core remains usable against EOSIO chains.
 - When show_payer is set on a KV row, payer is captured into
   ram_payers in the same way as the legacy {data, payer} path.
   Missing payer fields coerce to an empty Name rather than
   throwing.

Tests (15 new cases, 304/304 sdk-core suite green):

 packages/sdk-core/tests/chain/Abi.test.ts (8 cases)
  - Round-trips a table with table_id + empty secondary_indexes
  - Round-trips a long table name (>12 chars) — the whole reason
    name was widened
  - Round-trips secondary_indexes with checksum256 key_type
    (the case the wire-cdt #49 abigen fix unblocked end-to-end)
  - table_id 0 and 65535 boundary values
  - Missing table_id defaults to 0 on encode + decode
  - protobuf_types extension is consumed without affecting enums
  - Encoder always emits the empty protobuf_types trailer
  - Multi-table ABI end-to-end round-trip (structs + actions +
    secondary_indexes)

 packages/sdk-core/tests/api/v1/Chain.test.ts (7 cases)
  - Unwraps the new {key, value} shape into plain rows
  - Unwraps the new shape with show_payer + captures ram_payers
  - Preserves legacy plain-row shape from EOSIO chains
  - Preserves legacy {data, payer} show_payer shape from EOSIO
  - Empty rows array works for both shapes
  - Missing payer in new shape coerces to empty name (no throw)
  - Uses an in-memory MockProvider so no network access required

Out of scope:
 - packages/sdk-core/src/resources/{Ram,Rex,Powerup}.ts call
   get_table_rows for tables (rammarket, rex_pool, powup_state) that
   do not exist in wire-sysio and have not for some time. These
   resources were already dead on Wire chains regardless of any of
   the in-flight wire-sysio PRs and remain dead after this commit.
 - packages/sdk-core/src/types/SystemContractTypes.ts is auto-
   generated and would benefit from a regen pass after sysio.token's
   migration to kv::scoped_table merges (Wire-Network/wire-sysio#291),
   but the field schemas of the structs it generates do not depend
   on the binary table_def layout.

Companion PRs:
 - Wire-Network/wire-sysio#288 — chain-side table_id namespace isolation
 - Wire-Network/wire-sysio#290 — unified get_table_rows API
 - Wire-Network/wire-sysio#291 — sysio.token migrated to kv::scoped_table
 - Wire-Network/wire-cdt#49 — kv::scoped_table + abigen secondary_indexes
 - Wire-Network/wirejs-native#4 — TS schema mirror for abi_def
 - Wire-Network/abieos#12 — abieos C API + binary format update
 - Wire-Network/node-abieos#8 — bumps abieos + drops the lossy uint64 round-trip
 - Wire-Network/hyperion-history-api#9 — KV delta handler + AbiDefinitions

@dtaghavi dtaghavi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

Overview

This PR adapts abieos to mirror wire-sysio PR #288's binary format change: table_def.name widens from sysio::name (uint64) to std::string, gains table_id (uint16 DJB2 hash) and secondary_indexes, and a forward-compat protobuf_types extension is added to abi_def. The C API's abieos_get_type_for_table is updated from uint64_t table to const char* table. The rationale and companion PRs are well-documented.


Correctness & Logic

abieos_get_type_for_table null-check placement — The null guard is good, but the function will still crash with a SIGSEGV if a caller passes a non-null but non-null-terminated char*. This is a C API boundary; consider also documenting that the string must be null-terminated in the header comment.

std::string_view{table} lifetimetable_it->second.c_str() returns a pointer owned by the map value. If a concurrent writer reloads the ABI between the find and the return, the pointer dangles. This is an existing issue with the function (not introduced here), but worth noting since the signature change makes the function more likely to be called from JS/native bindings with looser threading assumptions.

secondary_indexes in abi.cpp — The diff doesn't show the convert(abi_def, abi) body; it would be useful to confirm that secondary_indexes is either fully consumed or explicitly skipped. If it's silently ignored during conversion, add a comment saying so — otherwise future readers will wonder whether it's a bug.

protobuf_types emissionto_json_write_helper(def.protobuf_types.value, ...) emits "protobuf_types": "" when the field was absent in the binary input. For variants/action_results/enums (vectors), emitting an empty array is harmless; for a string field, emitting "" could confuse a consumer that checks if (protobuf_types) for presence. Consider guarding with if (def.protobuf_types.exists) or equivalent.


Breaking Change Concerns

C API binary-compatibility — Changing uint64_t table to const char* is a silent ABI break on any platform where both parameters fit in the same register class. Code compiled against the old header that dynamically links to the new library will misbehave without a linker error. A symbol-version bump (@@ABIEOS_2) or a version macro (ABIEOS_VERSION) would let consumers detect the mismatch at build time.

action_types not updatedabi::action_types remains std::map<sysio::name, std::string> while table_types and abi_types now use std::less<>. This inconsistency is likely intentional (actions are always 12-char names), but a short comment would prevent someone from "fixing" it later by accident.


Test Coverage

  • No new test exercises the updated abieos_get_type_for_table with a string argument, a long (>12 char) table name, or the null-table error path.
  • No round-trip test for protobuf_types (serialize → deserialize → compare).
  • No test that a secondary_indexes-bearing ABI deserializes cleanly.

The tokenHexAbi regeneration is the primary coverage; it would be helpful to add at least one targeted test for each of the above.


Minor / Style

  • Comment in test.cpp references /tmp/regen_token_hex (a temp file). The regeneration script should either be committed to the repo or replaced with a reproducible make target / inline command so future maintainers can re-derive the hex without guessing.
  • table_id comment says "DJB2 hash of the table name % 65536" — worth noting the collision domain (65 536 slots) in a comment on index_def::table_id, or at minimum pointing to where wire-sysio guarantees uniqueness, so abieos maintainers know not to rely on it as a true key.
  • Several new comments include wire-sysio PR references (e.g. // Wire-sysio PR Wire-Network/wire-sysio#288). These are useful now but will rot. Keep them in the commit message / PR description rather than inline, or at least use a stable tag rather than a PR number.

Summary

The core logic is sound and the transparent-comparator upgrades (std::less<> on jobject, table_types, abi_types) are a clean, low-risk improvement. The main concerns before merging are:

  1. Versioning / detection of the breaking C API change.
  2. Test coverage for the new string-based table lookup, long names, and null input.
  3. Clarification on whether secondary_indexes is consumed or silently skipped in convert().
  4. protobuf_types JSON emission guard when the field is absent.

Everything else is polish.

- Guard protobuf_types JSON emission so an absent/empty schema no longer
  round-trips as "protobuf_types":""
- Note in convert(abi_def, abi) that table_id and secondary_indexes are
  intentionally not consumed by abieos (kept in abi_def for faithful
  binary round-trips; resolution uses the primary type only)
- Document that abieos_get_type_for_table requires a null-terminated,
  non-null table string; null input returns a descriptive error
- Drop inline Wire-Network/wire-sysio#288 refs (comment rot); rationale
  stays in the PR description
- Note index_def::table_id has a 65 536-slot collision domain with
  uniqueness guaranteed chain-side, not by abieos
- Comment on abi::action_types explaining why it stays keyed on
  sysio::name while table_types moved to std::string
- Bump project VERSION 0.1 -> 0.2 to flag the breaking C API change
  (abieos_get_type_for_table: uint64_t -> const char*)
- Add tests:
    check_token_abi_hex_round_trip -- tokenAbiJson -> bin must equal
      tokenHexAbi, so drift fails CI with a paste-ready diff (replaces
      the /tmp/regen_token_hex reference)
    check_long_table_names -- >12-char table name with a
      secondary_indexes entry round-trips through
      abieos_abi_json_to_bin / abieos_abi_bin_to_json and is reachable
      via abieos_get_type_for_table
    check_get_type_for_table_errors -- short-name lookup,
      null-table error, unknown-contract error
    check_protobuf_types_round_trip -- content preserved when present,
      field omitted from JSON when empty
@heifner
heifner requested a review from dtaghavi April 16, 2026 20:21

@dtaghavi dtaghavi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Follow-up Review

All four blocking items from the previous round have been addressed. The PR is in good shape; a few small notes below.


What was fixed ✓

  • protobuf_types emission guard — the if (!def.protobuf_types.value.empty()) guard is correct and the comment explains the consumer-facing rationale.
  • secondary_indexes not consumed — the explicit comment in abi.cpp makes the intentional skip clear for future maintainers.
  • table_id collision domain — well-documented on index_def::table_id; the "uniqueness guaranteed chain-side" note sets the right expectation.
  • action_types inconsistency — the new comment on abi::action_types explains exactly why the key type difference is intentional.
  • Null-termination requirement — documented clearly in abieos.h.
  • /tmp/regen_token_hex — replaced with an inline, self-contained regeneration recipe in the comment; the new check_token_abi_hex_round_trip test closes the loop by failing with a ready-to-paste hex diff on mismatch.
  • Test coverage — four new test functions cover all the gaps from the prior review (hex round-trip, long table names, abieos_get_type_for_table error paths, protobuf_types presence/absence).

Remaining minor notes

CMake version bump onlyCMakeLists.txt is bumped 0.1 → 0.2, which is good for build systems. However, C consumers who include abieos.h directly (especially when using the shared library as a system package) have no compile-time way to detect the uint64_t → const char* signature change. A one-liner like #define ABIEOS_VERSION 2 in abieos.h would let them guard with #if ABIEOS_VERSION >= 2.

Stale call in check_protobuf_types_round_trip — lines around "Absence case":

check_context(context, abieos_set_abi(context, contract, R"({"version":"sysio::abi/1.2"})"));
check_context(context, abieos_abi_json_to_bin(context, R"({"version":"sysio::abi/1.2","protobuf_types":""})"));

The abieos_set_abi call's result is immediately overwritten by abieos_abi_json_to_bin into the same context buffer — the set-abi output is never used and can be removed without changing test behavior.

c_str() lifetime (pre-existing)abieos_get_type_for_table still returns table_it->second.c_str(), a pointer into the live map. This was pre-existing and is out of scope here, but worth tracking as a follow-on: a concurrent ABI reload would dangle the returned pointer for the caller.


Overall

The implementation is correct, the binary format alignment is well-guarded by comments, and the new tests are thorough. The two items above are polish-level. LGTM to merge once the stale abieos_set_abi line is dropped (or left with an explanatory comment if there's a reason for it).

Address follow-up PR #12 review:

- Remove the redundant abieos_set_abi call in
  check_protobuf_types_round_trip. Its output is immediately
  overwritten by the next abieos_abi_json_to_bin into the same
  context buffer, so it had no effect on the test.
- Remove the now-unused `contract` local.
- Make the absence-case loop actually exercise both scenarios
  described by its comment: the "fully omitted" field and the
  "explicit empty string" input must both round-trip to JSON
  that omits the key entirely.

The #define ABIEOS_VERSION suggestion from the review is
intentionally not taken; CMake version bump is considered
sufficient.
@heifner
heifner requested a review from dtaghavi April 16, 2026 20:56

@dtaghavi dtaghavi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Follow-up Review (round 3)

What changed

The only substantive change from the previous round is in check_protobuf_types_round_trip. The stale abieos_set_abi call is gone and has been replaced with a loop over two "absence" inputs:

const char* absent_inputs[] = {
    R"({"version":"sysio::abi/1.2","protobuf_types":""})", // explicit empty
    R"({"version":"sysio::abi/1.2"})",                     // fully omitted
};

This is better than what we asked for — it now tests both the explicit-empty-string and the field-entirely-absent paths in one pass, which is the right thing to assert. Good fix.

Everything else is unchanged from the previous version (all prior blocking items remain resolved).


One open item

The ABIEOS_VERSION header macro mentioned last round is still absent. The CMake version is 0.2, but a C consumer who builds against a system-installed header has no #if ABIEOS_VERSION >= 2 guard available to detect the uint64_t → const char* signature change at compile time. Adding #define ABIEOS_VERSION 2 to abieos.h is a one-liner and closes that gap permanently.

This is the only remaining note. It's non-blocking if the Wire ecosystem is the only consumer (the PR description covers that), but worth doing while the change is fresh.


Overall

Implementation is correct, binary alignment is well-guarded, tests are thorough and now self-documenting. LGTM to merge.

@dtaghavi
dtaghavi self-requested a review April 16, 2026 21:02
@heifner

heifner commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up Review (round 3)

One open item

The ABIEOS_VERSION header macro mentioned last round is still absent. The CMake version is 0.2, but a C consumer who builds against a system-installed header has no #if ABIEOS_VERSION >= 2 guard available to detect the uint64_t → const char* signature change at compile time. Adding #define ABIEOS_VERSION 2 to abieos.h is a one-liner and closes that gap permanently.

I don't think we should do this. It is new functionality.

jglanz pushed a commit to Wire-Network/wire-libraries-ts that referenced this pull request Apr 18, 2026
Wire-sysio PR Wire-Network/wire-sysio#288 widened table_def.name from
sysio::name (uint64) to a free-form std::string and appended two new
fields — table_id (uint16, DJB2 hash of the table name % 65536) and
secondary_indexes (vector<index_def>) — for KV-table per-table
namespace isolation. The wire format break means any sdk-core caller
that loads a contract ABI from on-chain via get_raw_abi (or any other
binary-encoded source) parses misaligned starting at the first table
field — name reads the wrong bytes, every subsequent field is shifted,
and ricardian_clauses ends up reading garbage.

PR Wire-Network/wire-sysio#290 separately changed the get_table_rows
HTTP response shape for KV-backed tables. Each row used to be the
decoded struct directly (or {data, payer} when show_payer was set);
the unified endpoint now returns {key, value, payer?} per row. The
ChainAPI wrapper used to destructure {data, payer} on show_payer and
then map rows through Serializer.decode for typed callers — both paths
break against the new shape.

This commit takes a hard break on the binary format (no fallback to
the legacy EOSIO 8-byte name encoding) and a backward-compatible
unwrap on the HTTP response shape.

packages/sdk-core/src/chain/Abi.ts:
 - new ABI.Index interface (name, key_type, table_id) mirroring
   sysio::chain::index_def in wire-sysio's
   libraries/chain/include/sysio/chain/abi_def.hpp.
 - ABI.Table gains optional table_id (uint16) and secondary_indexes
   (Index[]) fields. Optional so hand-built test fixtures and ABIs
   constructed programmatically don't need to specify them; the
   encoder defaults to 0 / [] when omitted.
 - fromABI() reads name as a length-prefixed string instead of an
   8-byte sysio::name uint64, then consumes table_id (uint16 LE) and
   secondary_indexes (vector<index_def>) after the existing type
   field. Also reads a trailing protobuf_types extension after enums
   so that any subsequent extension appended to abi_def parses
   cleanly to EOF.
 - toABI() mirrors the parser: writes table_def.name via
   encoder.writeString(String(table.name)) (the String() coercion
   keeps NameType-typed callers compiling), then table_id and
   secondary_indexes, and finally the empty protobuf_types string.

packages/sdk-core/src/api/v1/Chain.ts:
 - get_table_rows() now detects the unified KV row shape — each row
   has both `key` and `value` keys — and unwraps to the inner
   value before downstream processing. The old EOSIO shapes
   (decoded struct directly, or {data, payer} on show_payer) are
   left untouched, so sdk-core remains usable against EOSIO chains.
 - When show_payer is set on a KV row, payer is captured into
   ram_payers in the same way as the legacy {data, payer} path.
   Missing payer fields coerce to an empty Name rather than
   throwing.

Tests (15 new cases, 304/304 sdk-core suite green):

 packages/sdk-core/tests/chain/Abi.test.ts (8 cases)
  - Round-trips a table with table_id + empty secondary_indexes
  - Round-trips a long table name (>12 chars) — the whole reason
    name was widened
  - Round-trips secondary_indexes with checksum256 key_type
    (the case the wire-cdt #49 abigen fix unblocked end-to-end)
  - table_id 0 and 65535 boundary values
  - Missing table_id defaults to 0 on encode + decode
  - protobuf_types extension is consumed without affecting enums
  - Encoder always emits the empty protobuf_types trailer
  - Multi-table ABI end-to-end round-trip (structs + actions +
    secondary_indexes)

 packages/sdk-core/tests/api/v1/Chain.test.ts (7 cases)
  - Unwraps the new {key, value} shape into plain rows
  - Unwraps the new shape with show_payer + captures ram_payers
  - Preserves legacy plain-row shape from EOSIO chains
  - Preserves legacy {data, payer} show_payer shape from EOSIO
  - Empty rows array works for both shapes
  - Missing payer in new shape coerces to empty name (no throw)
  - Uses an in-memory MockProvider so no network access required

Out of scope:
 - packages/sdk-core/src/resources/{Ram,Rex,Powerup}.ts call
   get_table_rows for tables (rammarket, rex_pool, powup_state) that
   do not exist in wire-sysio and have not for some time. These
   resources were already dead on Wire chains regardless of any of
   the in-flight wire-sysio PRs and remain dead after this commit.
 - packages/sdk-core/src/types/SystemContractTypes.ts is auto-
   generated and would benefit from a regen pass after sysio.token's
   migration to kv::scoped_table merges (Wire-Network/wire-sysio#291),
   but the field schemas of the structs it generates do not depend
   on the binary table_def layout.

Companion PRs:
 - Wire-Network/wire-sysio#288 — chain-side table_id namespace isolation
 - Wire-Network/wire-sysio#290 — unified get_table_rows API
 - Wire-Network/wire-sysio#291 — sysio.token migrated to kv::scoped_table
 - Wire-Network/wire-cdt#49 — kv::scoped_table + abigen secondary_indexes
 - Wire-Network/wirejs-native#4 — TS schema mirror for abi_def
 - Wire-Network/abieos#12 — abieos C API + binary format update
 - Wire-Network/node-abieos#8 — bumps abieos + drops the lossy uint64 round-trip
 - Wire-Network/hyperion-history-api#9 — KV delta handler + AbiDefinitions
@heifner
heifner merged commit 7ca8512 into master Apr 20, 2026
13 of 14 checks passed
@heifner
heifner deleted the feature/wire-sysio-table-id branch April 20, 2026 19:51
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.

2 participants