Skip to content

Commit

Permalink
fix Telescope encoding Decimal (#10080)
Browse files Browse the repository at this point in the history
closes: #10072

## Description

We can't use cosmjs/math because it uses bn.js which is huge. They've [decided not to remove it from cosmjs/math](cosmos/cosmjs#203) so we patched Telescope to use its own Decimal module. However the subset chosen started breaking with the use of Query modules.

Updating the patch was a giant pain so instead this sources from a fork, https://github.com/agoric-labs/telescope/tree/agoric-safe/

When updating that fork, the assets have to be build and pushed because they're not published to NPM.

Also, the code built should be compatible with what's currently published to NPM because the other packages will be sourced from there. Note that this fork is currently behind upstream (agoric-labs/telescope@agoric-safe...cosmology-tech:telescope:main ) because [this commit](agoric-labs/telescope@f1796e9) requires an unpublished [@cosmology/utils](https://www.npmjs.com/package/@cosmology/utils) (symption is `toPosixPath` undefined).


### Security Considerations
sources from a repo using gitpkg.vercel.app which could modify the contents in transit. The yarn.lock doesn't have an integrity check for Git sources. However this code is only used for codegen so any malice or even error would be detected in the new output.

### Scaling Considerations
n/a

### Documentation Considerations
The fork updating process is not obvious. I hope the PR description suffices.

### Testing Considerations
The new `build QueryDelegatorDelegationsResponse` test fails under master, showing that it solves the problem.

### Upgrade Considerations
n/a
  • Loading branch information
mergify[bot] authored Sep 16, 2024
2 parents a66b840 + c1c5a6f commit 67a57a4
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 1,123 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ _testoutput.txt


junit.xml
endo-sha.txt
endo-sha.txt
.aider*
10 changes: 9 additions & 1 deletion packages/cosmic-proto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@
"types": "./dist/codegen/cosmos/bank/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/bank/v1beta1/tx.js"
},
"./cosmos/distribution/v1beta1/query.js": {
"types": "./dist/codegen/cosmos/distribution/v1beta1/query.d.ts",
"default": "./dist/codegen/cosmos/distribution/v1beta1/query.js"
},
"./cosmos/distribution/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/distribution/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/distribution/v1beta1/tx.js"
},
"./cosmos/staking/v1beta1/query.js": {
"types": "./dist/codegen/cosmos/staking/v1beta1/query.d.ts",
"default": "./dist/codegen/cosmos/staking/v1beta1/query.js"
},
"./cosmos/staking/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/staking/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/staking/v1beta1/tx.js"
Expand Down Expand Up @@ -136,7 +144,7 @@
"devDependencies": {
"@agoric/cosmos": "^0.34.1",
"@ava/typescript": "^4.1.0",
"@cosmology/telescope": "^1.7.1",
"@cosmology/telescope": "https://gitpkg.vercel.app/agoric-labs/telescope/packages/telescope?8d2c2f6ba637a5578eead09a7368dc41c262a9d0",
"@endo/bundle-source": "^3.4.0",
"@endo/import-bundle": "^1.2.2",
"ava": "^5.3.1",
Expand Down
14 changes: 7 additions & 7 deletions packages/cosmic-proto/src/codegen/binary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/
Expand Down Expand Up @@ -100,7 +100,7 @@ export class BinaryReader implements IBinaryReader {
len: number;

assertBounds(): void {
if (this.pos > this.len) throw RangeError('premature EOF');
if (this.pos > this.len) throw new RangeError('premature EOF');
}

constructor(buf?: ArrayLike<number>) {
Expand All @@ -115,7 +115,7 @@ export class BinaryReader implements IBinaryReader {
fieldNo = tag >>> 3,
wireType = tag & 7;
if (fieldNo <= 0 || wireType < 0 || wireType > 5)
throw Error(
throw new Error(
'illegal tag: field no ' + fieldNo + ' wire type ' + wireType,
);
return [fieldNo, wireType, tag];
Expand Down Expand Up @@ -214,11 +214,11 @@ export class BinaryReader implements IBinaryReader {
}

float(): number {
throw Error('float not supported');
throw new Error('float not supported');
}

double(): number {
throw Error('double not supported');
throw new Error('double not supported');
}

bool(): boolean {
Expand Down Expand Up @@ -466,11 +466,11 @@ export class BinaryWriter implements IBinaryWriter {
sfixed32 = BinaryWriter.prototype.fixed32;

float(value: number): BinaryWriter {
throw Error('float not supported' + value);
throw new Error('float not supported' + value);
}

double(value: number): BinaryWriter {
throw Error('double not supported' + value);
throw new Error('double not supported' + value);
}

bytes(value: Uint8Array): BinaryWriter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export enum FieldDescriptorProto_Type {
* treat group fields as unknown fields.
*/
TYPE_GROUP = 10,
/** TYPE_MESSAGE - Length-delimited aggregate. */
TYPE_MESSAGE = 11,
/** TYPE_BYTES - New in version 2. */
TYPE_BYTES = 12,
Expand Down Expand Up @@ -198,12 +199,9 @@ export function fieldDescriptorProto_LabelToJSON(
}
/** Generated classes can be optimized for speed or code size. */
export enum FileOptions_OptimizeMode {
/**
* SPEED - Generate complete code for parsing, serialization,
* etc.
*/
/** SPEED - Generate complete code for parsing, serialization, */
SPEED = 1,
/** CODE_SIZE - Use ReflectionOps to implement these methods. */
/** CODE_SIZE - etc. */
CODE_SIZE = 2,
/** LITE_RUNTIME - Generate code using MessageLite and the lite runtime. */
LITE_RUNTIME = 3,
Expand Down Expand Up @@ -393,6 +391,7 @@ export interface FileDescriptorSetSDKType {
export interface FileDescriptorProto {
/** file name, relative to root of source tree */
name: string;
/** e.g. "foo", "foo.bar", etc. */
package: string;
/** Names of files imported by this file. */
dependency: string[];
Expand Down
72 changes: 60 additions & 12 deletions packages/cosmic-proto/src/codegen/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/
Expand Down Expand Up @@ -35,7 +35,7 @@ export function omitDefault<T extends string | number | bigint | boolean>(
return input === BigInt(0) ? undefined : input;
}

throw Error(`Got unsupported type ${typeof input}`);
throw new Error(`Got unsupported type ${typeof input}`);
}

interface Duration {
Expand Down Expand Up @@ -223,9 +223,10 @@ function numberToLong(number: number) {
return BigInt(Math.trunc(number));
}

// START agoric-sdk patch
// The largest value we need is 18 (Ether).
const maxFractionalDigits = 30;
// Subset of Decimal in @cosmjs/math

/**
* A type for arbitrary precision, non-negative decimals.
*
Expand All @@ -241,7 +242,9 @@ export class Decimal {
const badCharacter = input.match(/[^0-9.]/);
if (badCharacter) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
throw Error(`Invalid character at position ${badCharacter.index! + 1}`);
throw new Error(
`Invalid character at position ${badCharacter.index! + 1}`,
);
}

let whole: string;
Expand All @@ -250,7 +253,7 @@ export class Decimal {
if (input === '') {
whole = '0';
fractional = '';
} else if (input.search(/./) === -1) {
} else if (input.search(/\./) === -1) {
// integer format, no separator
whole = input;
fractional = '';
Expand All @@ -259,21 +262,21 @@ export class Decimal {
switch (parts.length) {
case 0:
case 1:
throw Error(
throw new Error(
'Fewer than two elements in split result. This must not happen here.',
);
case 2:
if (!parts[1]) throw Error('Fractional part missing');
if (!parts[1]) throw new Error('Fractional part missing');
whole = parts[0];
fractional = parts[1].replace(/0+$/, '');
break;
default:
throw Error('More than one separator found');
throw new Error('More than one separator found');
}
}

if (fractional.length > fractionalDigits) {
throw Error('Got more fractional digits than supported');
throw new Error('Got more fractional digits than supported');
}

const quantity = `${whole}${fractional.padEnd(fractionalDigits, '0')}`;
Expand All @@ -291,11 +294,56 @@ export class Decimal {

private static verifyFractionalDigits(fractionalDigits: number): void {
if (!Number.isInteger(fractionalDigits))
throw Error('Fractional digits is not an integer');
throw new Error('Fractional digits is not an integer');
if (fractionalDigits < 0)
throw Error('Fractional digits must not be negative');
throw new Error('Fractional digits must not be negative');
if (fractionalDigits > maxFractionalDigits) {
throw Error(`Fractional digits must not exceed ${maxFractionalDigits}`);
throw new Error(
`Fractional digits must not exceed ${maxFractionalDigits}`,
);
}
}

public get atomics(): string {
return this.data.atomics.toString();
}

public get fractionalDigits(): number {
return this.data.fractionalDigits;
}

private readonly data: {
readonly atomics: bigint;
readonly fractionalDigits: number;
};

private constructor(atomics: string, fractionalDigits: number) {
if (!atomics.match(/^[0-9]+$/)) {
throw new Error(
'Invalid string format. Only non-negative integers in decimal representation supported.',
);
}

this.data = {
atomics: BigInt(atomics),
fractionalDigits: fractionalDigits,
};
}

public toString(): string {
const factor = BigInt(10) ** BigInt(this.data.fractionalDigits);
const whole = this.data.atomics / factor;
const fractional = this.data.atomics % factor;

if (fractional === 0n) {
return whole.toString();
} else {
const fullFractionalPart = fractional
.toString()
.padStart(this.data.fractionalDigits, '0');
const trimmedFractionalPart = fullFractionalPart.replace(/0+$/, '');
return `${whole.toString()}.${trimmedFractionalPart}`;
}
}
}
// END agoric-sdk patch
2 changes: 1 addition & 1 deletion packages/cosmic-proto/src/codegen/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/
Expand Down
14 changes: 8 additions & 6 deletions packages/cosmic-proto/src/codegen/json-safe.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/

export type JsonSafe<T> = {
[Prop in keyof T]: T[Prop] extends Uint8Array | bigint | Date
? string
: T[Prop];
};
export type JsonSafe<T> = T extends Uint8Array | bigint | Date
? string
: T extends Array<infer U>
? Array<JsonSafe<U>>
: T extends object
? { [K in keyof T]: JsonSafe<T[K]> }
: T;
7 changes: 2 additions & 5 deletions packages/cosmic-proto/src/codegen/tendermint/abci/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,12 +849,9 @@ export interface TxResultSDKType {
}
/** Validator */
export interface Validator {
/**
* The first 20 bytes of SHA256(public key)
* PubKey pub_key = 2 [(gogoproto.nullable)=false];
*/
/** The first 20 bytes of SHA256(public key) */
address: Uint8Array;
/** The voting power */
/** PubKey pub_key = 2 [(gogoproto.nullable)=false]; */
power: bigint;
}
export interface ValidatorProtoMsg {
Expand Down
2 changes: 2 additions & 0 deletions packages/cosmic-proto/src/codegen/tendermint/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export interface Header {
lastBlockId: BlockID;
/** hashes of block data */
lastCommitHash: Uint8Array;
/** transactions */
dataHash: Uint8Array;
/** hashes from the app output from the prev block */
validatorsHash: Uint8Array;
Expand All @@ -167,6 +168,7 @@ export interface Header {
consensusHash: Uint8Array;
/** state after txs from the previous block */
appHash: Uint8Array;
/** root hash of all results from the txs from the previous block */
lastResultsHash: Uint8Array;
/** consensus info */
evidenceHash: Uint8Array;
Expand Down
2 changes: 1 addition & 1 deletion packages/cosmic-proto/src/codegen/utf8.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/
Expand Down
6 changes: 3 additions & 3 deletions packages/cosmic-proto/src/codegen/varint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ts-nocheck
/**
* This file and any referenced files were automatically generated by @cosmology/telescope@1.7.1
* This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
* and run the transpile command or npm scripts command that is used to regenerate this bundle.
*/
Expand Down Expand Up @@ -86,7 +86,7 @@ export function varint64read(this: ReaderLike): [number, number] {
}
}

throw Error('invalid varint');
throw new Error('invalid varint');
}

/**
Expand Down Expand Up @@ -353,7 +353,7 @@ export function varint32read(this: ReaderLike): number {
for (let readBytes = 5; (b & 0x80) !== 0 && readBytes < 10; readBytes++)
b = this.buf[this.pos++];

if ((b & 0x80) != 0) throw Error('invalid varint');
if ((b & 0x80) != 0) throw new Error('invalid varint');

this.assertBounds();

Expand Down
3 changes: 1 addition & 2 deletions packages/cosmic-proto/test/helpers.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,5 @@ import type { Timestamp } from '../src/codegen/google/protobuf/timestamp.js';
null as any;
expectType<Timestamp>(response.completionTime);
const responseJson: JsonSafe<typeof response> = null as any;
// FIXME: should be a string. UNTIL: github.com/cosmology-tech/telescope/pull/632
expectType<bigint>(responseJson.completionTime.seconds);
expectType<string>(responseJson.completionTime.seconds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,10 @@ export const prepareLocalOrchestrationAccountKit = (
*/
queryBalancesWatcher: {
/**
* @param {ResponseTo<
* TypedJson<'/cosmos.bank.v1beta1.QueryAllBalancesRequest'>
* @param {JsonSafe<
* ResponseTo<
* TypedJson<'/cosmos.bank.v1beta1.QueryAllBalancesRequest'>
* >
* >} result
* @returns {DenomAmount[]}
*/
Expand Down
5 changes: 4 additions & 1 deletion packages/orchestration/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ export const chainFacadeMethods = harden({
/**
* for google/protobuf/timestamp.proto, not to be confused with TimestampShape
* from `@agoric/time`
*
* `seconds` is a big integer but since it goes through JSON it is encoded as
* string
*/
export const TimestampProtoShape = { seconds: M.nat(), nanos: M.number() };
export const TimestampProtoShape = { seconds: M.string(), nanos: M.number() };

/**
* see {@link TxBody} for more details
Expand Down
Loading

0 comments on commit 67a57a4

Please sign in to comment.