Skip to content

#341 registerUpgrade() references createTranslateFromMapping which is… - #351

Open
felladaniel36-hash wants to merge 2 commits into
Open-audit-foundation:mainfrom
felladaniel36-hash:#341-registerUpgrade()-references-createTranslateFromMapping-which-is-not-defined-anywhere-in-the-codebase-FIX
Open

#341 registerUpgrade() references createTranslateFromMapping which is…#351
felladaniel36-hash wants to merge 2 commits into
Open-audit-foundation:mainfrom
felladaniel36-hash:#341-registerUpgrade()-references-createTranslateFromMapping-which-is-not-defined-anywhere-in-the-codebase-FIX

Conversation

@felladaniel36-hash

Copy link
Copy Markdown
Contributor

📋 Issue Summary

Problem: registerUpgrade in lib/translator/registry.ts attempted to build runtime translation blueprints using createTranslateFromMapping, but the translation path was incomplete and unsafe:

  • The mapping helper was missing/incomplete.
  • Required decoding and interpolation utilities were not correctly connected.
  • eventMappings used any[], bypassing TypeScript validation.
  • Mapping translators were recreated for every translated event.
  • Templates containing dotted parameters such as {from.short} and {amount.formatted} were not interpolated correctly.
  • Runtime contract upgrades could fail instead of selecting the correct event schema by ledger boundary.

Solution: Implemented a fully typed event-mapping translation pipeline, connected it to registerUpgrade, preserved schema version metadata, added ledger-boundary tests, and ensured cache invalidation works after registering an upgrade.


✅ Changes Made

Modified Files

  1. lib/translator/registry.ts

    • Implemented the typed createTranslateFromMapping translation path.
    • Replaced eventMappings: any[] with:
      eventMappings: readonly EventMappingDefinition[]
    • Imported and connected:
      • decodeAddress
      • decodeAmount
      • decodeEventName
      • interpolateTemplate
    • Added positional topic and event-data decoding.
    • Added support for:
      • {field}
      • {field.short}
      • {field.formatted}
    • Added language-specific template selection with English fallback.
    • Compiles mapping translators once during registration.
    • Stores version and validFromLedger on generated upgrade blueprints.
    • Removed the unsafe any cast previously used to read blueprint versions.
    • Preserved contract-specific resolution-cache invalidation.
  2. lib/translator/types.ts

    • Added typed runtime mapping definitions:
      • EventMappingFieldType
      • EventMappingField
      • EventMappingStructure
      • EventMappingDefinition
    • Defined the supported mapping field types without using any.
  3. lib/translator/registry.versioning.test.ts

    • Typed all upgrade mapping fixtures.
    • Added direct resolveSchema assertions.
    • Added coverage for events:
      • Before an upgrade boundary
      • At an upgrade boundary
      • Immediately before the next boundary
      • At the next boundary
      • After the latest registered upgrade
    • Retained cache-invalidation and historical-replay coverage.
  4. lib/translator/core.ts

    • Added interpolation support for dotted template variables.
    • Repaired upstream module-load defects required for runtime mapping translation.
    • Restored functional address decoding with a safe fallback for abbreviated test values.

New Files Created

None.


🧩 Typed Runtime Mapping API

The public API now accepts strongly typed mapping definitions:

export function registerUpgrade(
  contractId: string,
  version: string,
  fromLedger: number,
  eventMappings: readonly EventMappingDefinition[]
): void;

Example:

const mappings: EventMappingDefinition[] = [
  {
    topics: ["transfer"],
    event_structure: {
      topics: [
        { name: "from", type: "address" },
        { name: "to", type: "address" },
      ],
      data: {
        name: "amount",
        type: "i128",
      },
    },
    english_template:
      "{from.short} transferred {amount.formatted} to {to.short}",
  },
];

registerUpgrade(
  contractId,
  "2.0.0",
  500,
  mappings
);

No any[] parameter is used.


🔄 Runtime Upgrade Flow

flowchart TD
    RegisterUpgrade[registerUpgrade called]
    TypedMappings[Validate typed event mappings]
    Compile[Compile mapping translators once]
    Schema[Create versioned translation blueprint]
    Sort[Sort schemas by validFromLedger]
    Boundaries[Recalculate validToLedger boundaries]
    Cache[Invalidate contract resolution cache]
    Resolve[resolveSchema receives event ledger]
    Translate[Use matching schema translator]

    RegisterUpgrade --> TypedMappings
    TypedMappings --> Compile
    Compile --> Schema
    Schema --> Sort
    Sort --> Boundaries
    Boundaries --> Cache
    Cache --> Resolve
    Resolve --> Translate
Loading

📐 Schema Boundary Behavior

Given these registrations:

registerUpgrade(contractId, "1.1.0", 100, v1Mappings);
registerUpgrade(contractId, "2.0.0", 500, v2Mappings);

The registry resolves schemas as follows:

Event ledger Selected schema
99 Original schema
100 1.1.0
499 1.1.0
500 2.0.0
10,000 2.0.0

The resulting ranges are:

Original schema: valid until ledger 99
Version 1.1.0:   valid from 100 through 499
Version 2.0.0:   valid from 500 onward

Historical events therefore continue using the schema that was active when they were emitted.


🌍 Template Support

The translator supports both the legacy English template property and language-specific templates.

Legacy format

{
  english_template: "{from.short} transferred to {to.short}"
}

Language-specific format

{
  templates: {
    en: "{from.short} transferred to {to.short}",
    es: "{from.short} transfirió a {to.short}",
    fr: "{from.short} a transféré à {to.short}",
  }
}

Template selection order:

mapping.templates?.[lang]
  ?? mapping.templates?.en
  ?? mapping.english_template

⚡ Performance Improvement

Before

A mapping translator was constructed inside the translation loop:

for (const mapping of eventMappings) {
  const result = createTranslateFromMapping(mapping)(event, lang);
}

This recreated closures every time an event was translated.

After

Mapping translators are compiled once when the upgrade is registered:

const translators = eventMappings.map(createTranslateFromMapping);

The event path only executes the precompiled translators:

for (const translate of translators) {
  const result = translate(event, lang);
  if (result) return result;
}

🧪 Testing

Version-selection tests

Executed:

npx vitest run lib/translator/registry.versioning.test.ts

Result:

Test Files  1 passed (1)
Tests       3 passed (3)

The tests prove:

  • registerUpgrade executes without the reported missing-function error.
  • Historical ledgers retain the previous schema.
  • Exact ledger boundaries select the new schema.
  • Later events select the latest schema.
  • Registering another upgrade invalidates stale resolution-cache entries.

Registry-focused test suite

Executed:

npx vitest run \
  lib/translator/registry.versioning.test.ts \
  lib/translator/registry.resolution-cache.test.ts \
  lib/translator/registry.fallback.test.ts

Result:

Test Files  3 passed (3)
Tests       7 passed (7)

Registry validation

Executed:

npm run validate:registry
npm run lint:registry

Result:

Registry validated successfully against schema.
ALL VALIDATION CHECKS PASSED
13 registry entries validated successfully.

Diff validation

Executed:

git diff --check

Result: Passed.


⚠️ Repository-Wide Validation Status

The issue-specific implementation and registry tests pass. However, the current upstream main branch contains unrelated pre-existing build, type-check, and test failures.

Full test suite

npm test

Result:

Test Files  12 failed | 16 passed
Tests       82 failed | 388 passed

The remaining failures involve unrelated areas such as:

  • Incomplete XDR map/vector decoders
  • Security parser metrics
  • Generic fallback decoding
  • Sanitization tests
  • Token-bucket timeout behavior

TypeScript

npx tsc --noEmit

The repository-wide check remains blocked by unrelated existing errors, including missing modules, invalid exports, API route typing, dashboard typing, indexer conflicts, and test typing errors.

Production build

npm run build

The build starts but is blocked by unrelated missing upstream modules and exports:

Can't resolve '../dag/engine'
Can't resolve '../dag/types'
Can't resolve '@/lib/db/clickhouse-ingest'
Can't resolve '@/lib/stellar/historical-ingester'
Can't resolve 'stellar-sdk/lib/soroban'
Export 'resilientStellarClient' does not exist

These failures are outside the files and behavior addressed by this PR.


🎯 Acceptance Criteria

Acceptance criterion Status
registerUpgrade can be called without the reported ReferenceError ✅ Passed
Mapping helper conforms to the blueprint translation signature ✅ Passed
eventMappings no longer uses any[] ✅ Passed
Typed mapping interfaces are available ✅ Passed
Schema selection is tested across ledger boundaries ✅ Passed
Resolution cache is invalidated after an upgrade ✅ Passed
Registry-focused tests pass ✅ Passed
Registry validation passes ✅ Passed
Full repository tsc --noEmit passes ⚠️ Blocked by unrelated upstream errors
Full repository build passes ⚠️ Blocked by unrelated missing modules
Every existing repository test passes ⚠️ Blocked by unrelated pre-existing failures

📊 Fix Metrics

  • Files modified: 4
  • New typed mapping definitions: 4
  • Registry-focused tests passing: 7/7
  • Versioning tests passing: 3/3
  • Ledger boundaries explicitly tested: 5
  • any parameters in registerUpgrade: 0
  • Runtime translator creation per event: Eliminated
  • Reported missing-function failure: Resolved

🔍 Key Review Areas

Reviewers should focus on:

  1. The new event-mapping types in lib/translator/types.ts.
  2. Mapping compilation in registerUpgrade.
  3. Positional topic/data decoding.
  4. Language fallback behavior.
  5. Schema range recalculation.
  6. Resolution-cache invalidation.
  7. Ledger-boundary assertions in registry.versioning.test.ts.

📌 Closes Issue

This fix resolves the issue where runtime contract upgrades could reference a missing or incomplete createTranslateFromMapping implementation and bypass TypeScript through any[].

Before: Runtime upgrade registration was unsafe and could fail during translation.

After: Runtime upgrades use typed mapping definitions, precompiled translators, language-aware templates, cache invalidation, and ledger-correct schema resolution.


Branch Information

  • Base: main
  • Type: Bug fix
  • Breaking changes: None for valid existing mapping declarations
  • Requires review: Translation mapping types and schema-boundary behavior

Ready for review, with unrelated upstream repository failures documented above.

CLOSE #341

…eFromMapping which is not defined anywhere in the codebase FIXED
@Osuochasam

Copy link
Copy Markdown
Collaborator

@felladaniel36-hash fix conflicts

…references-createTranslateFromMapping-which-is-not-defined-anywhere-in-the-codebase-FIX
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.

registerUpgrade() references createTranslateFromMapping which is not defined anywhere in the codebase

2 participants