Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decode delegation query response fails in Compartment due to undefined Decimal #9408

Closed
dckc opened this issue May 24, 2024 · 3 comments · Fixed by #9583
Closed

decode delegation query response fails in Compartment due to undefined Decimal #9408

dckc opened this issue May 24, 2024 · 3 comments · Fixed by #9583
Assignees
Labels
bug Something isn't working installation-bundling The bundling of installations

Comments

@dckc
Copy link
Member

dckc commented May 24, 2024

Describe the bug

While working on getDelegations for the unbondExample.contract.js, (#9070, #8863), I'm trying to use QueryDelegatorDelegationsResponse.decode(...) from @agoric/cosmic-proto inside a contract, but it fails due to Decimal from @cosmjs/math being undefined.

To Reproduce

I reduced it to running this in a compartment:

import { Decimal } from '@cosmjs/math';

export const fun = () => {
  const e18 = 10n ** 18n;
  return Decimal.fromAtomics(`${e18}`, 18);
};

3aa40db has a test for that and a test for using QueryDelegatorDelegationsResponse.decode(...).

  1. check out 3aa40db
  2. yarn; yarn build
  3. cd packages/orchestration
  4. yarn test test/tx-encoding.test.ts -m 'compartment*'
  5. 2 tests fail with Cannot read properties of undefined
yarn test run log
agoric-sdk/packages/orchestration$ yarn test  test/tx-encoding.test.ts -m 'compartment*'
yarn run v1.22.19
$ ava test/tx-encoding.test.ts -m 'compartment*'

  ✘ [fail]: compartment use of Decimal Rejected promise returned by test
    ℹ REJECTED from ava test("compartment use of Decimal"): (TypeError#1)
    ℹ TypeError#1: Cannot read properties of undefined (reading 'fromAtomics')
    ℹ   at fun (.../orchestration/test/decimalFun.js:5:16)
        at packages/orchestration/test/tx-encoding.test.ts:1:2989
      
  ✘ [fail]: compartment use of getDelegations() response Rejected promise returned by test
    ℹ REJECTED from ava test("compartment use of getDelegations() response"): (TypeError#2)
    ℹ TypeError#2: Cannot read properties of undefined (reading 'fromUserInput')
    ℹ   at Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/staking.js:982:16)
        at Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/staking.js:1587:12)
        at Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/query.js:991:20)
        at plz (.../orchestration/test/delegationFun.js:19:50)
        at packages/orchestration/test/tx-encoding.test.ts:1:3197
      
  ─

  compartment use of Decimal
  Rejected promise returned by test. Reason:

  TypeError {
    message: 'Cannot read properties of undefined (reading \'fromAtomics\')',
  }

  › fun (.../orchestration/test/decimalFun.js:5:16)
  › packages/orchestration/test/tx-encoding.test.ts:1:2989



  compartment use of getDelegations() response
  Rejected promise returned by test. Reason:

  TypeError {
    message: 'Cannot read properties of undefined (reading \'fromUserInput\')',
  }

  › Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/staking.js:982:16)
  › Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/staking.js:1587:12)
  › Object.encode (.../cosmic-proto/dist/codegen/cosmos/staking/v1beta1/query.js:991:20)
  › plz (.../orchestration/test/delegationFun.js:19:50)
  › packages/orchestration/test/tx-encoding.test.ts:1:3197

  ─

  2 tests failed
error Command failed with exit code 1.

Expected behavior

QueryDelegatorDelegationsResponse.decode(...) works inside a compartment just like it does outside a compartment.

Platform Environment

  • what version of the Agoric-SDK are you using? 3aa40db

cc @kriskowal @turadg @0xpatrickdev

@dckc dckc added the bug Something isn't working label May 24, 2024
@kriskowal
Copy link
Member

I have reproduced the error locally but not yet isolated a defect.

Given isolate-me.js

import { Decimal } from "@cosmjs/math";
import * as math from "@cosmjs/math";
console.log(Decimal);
console.log(math);
console.log(math.Decimal);

I get the correct behavior from Node.js and incorrect behavior from importBundle from @endo/import-bundle and importLocation from @endo/compartment-mapper. This is the narrower test harness that reproduces the failure:

import "ses";
import url from "url";
import fs from "fs";
import { importLocation } from "@endo/compartment-mapper";
import { makeReadPowers } from "@endo/compartment-mapper/node-powers.js";
const readPowers = makeReadPowers({ url, fs });
const location = new URL("isolate-me.js", import.meta.url).href;
importLocation(readPowers, location, {
  globals: { console },
});

The defect occurs at the boundary between an ESM/MJS module importing the CJS decimal module, which is compiled from TS. The generated code assigns to exports.Decimal twice. First, with void 0, then with the Decimal class. I attempted to isolate this case:

Given b.cjs

Object.defineProperty(exports, "__esModule", { value: true });
exports.b = void 0;
class B {}
exports.b = B;

Given a.mjs

import { b } from "./b.cjs";
console.log(b);

And this case works correctly in isolation under all of Node.js, importBundle, and importLocation.

@kriskowal
Copy link
Member

kriskowal commented Jun 22, 2024

The culprit turns out to be the intermediate index.js (CommonJS compiled from TS) that reexports Decimal using Object.defineProperty(exports… with a getter. This is hard to trap to propagate the live binding but I have a fix endojs/endo#2330

@dckc
Copy link
Member Author

dckc commented Jun 22, 2024

Thanks for the diagnosis!

Our approach to endo compat so far has mostly been to patch the offending parts of cosmjs; for example, bn.js patch

I'm not sure what would be the preferred tweak, but this seems to alleviate the symptoms:

$ cat patches/@cosmjs+math+0.32.3.patch
diff --git a/node_modules/@cosmjs/math/build/index.js b/node_modules/@cosmjs/math/build/index.js
index 1f812f6..41f6398 100644
--- a/node_modules/@cosmjs/math/build/index.js
+++ b/node_modules/@cosmjs/math/build/index.js
@@ -2,7 +2,8 @@
 Object.defineProperty(exports, "__esModule", { value: true });
 exports.Uint64 = exports.Uint53 = exports.Uint32 = exports.Int53 = exports.Decimal = void 0;
 var decimal_1 = require("./decimal");
-Object.defineProperty(exports, "Decimal", { enumerable: true, get: function () { return decimal_1.Decimal; } });
+// Object.defineProperty(exports, "Decimal", { enumerable: true, get: function () { return decimal_1.Decimal; } });
+exports.Decimal = decimal_1.Decimal;
 var integers_1 = require("./integers");
 Object.defineProperty(exports, "Int53", { enumerable: true, get: function () { return integers_1.Int53; } });
 Object.defineProperty(exports, "Uint32", { enumerable: true, get: function () { return integers_1.Uint32; } });

p.s. here's hoping for a .ts compiler that erases the types and does nothing else; especially no conversion to .cjs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working installation-bundling The bundling of installations
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants