Skip to content

Commit

Permalink
Rework version api + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 committed Jul 10, 2024
1 parent 2ccf03d commit c31e459
Show file tree
Hide file tree
Showing 9 changed files with 2,088 additions and 906 deletions.
2,728 changes: 2,027 additions & 701 deletions npm/pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions npm/src/bundled.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,4 @@ describe('BundledClient', () => {
});
expect(umStakingAsset.equals(registry.stakingAssetId)).toBeTruthy();
});

it('returns a version hash', async () => {
const hash = await bundled.version('penumbra-testnet-deimos-8');
expect('f533e27f727f6154783e9029e4644d6f0b71a97364048179b3699a7507afec22').toBe(hash);
});
});
19 changes: 2 additions & 17 deletions npm/src/bundled.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,20 @@
import { Registry } from './registry';
import { allJsonRegistries, JsonRegistry } from './json';
import { sha256Hash } from './utils/sha256';
import { allJsonRegistries } from './json';
import { swapIfPreviewChain } from './preview';
import * as GlobalsJson from '../../registry/globals.json';
import { globalsFromJson, RegistryGlobals } from './globals';

export class BundledClient {
get(chainId: string): Registry {
const jsonRegistry = this.getJsonRegistry(chainId);
return new Registry(jsonRegistry);
}

private getJsonRegistry(chainId: string): JsonRegistry {
const chainIdToIndex = swapIfPreviewChain(chainId);
const jsonRegistry = allJsonRegistries[chainIdToIndex];
if (!jsonRegistry) {
throw new Error(`Registry not found for ${chainIdToIndex}`);
}
return jsonRegistry;
return new Registry(jsonRegistry);
}

globals(): RegistryGlobals {
return globalsFromJson(GlobalsJson);
}

async version(chainId: string) {
const registry = this.getJsonRegistry(chainId);
const globals = this.globals();
const registryHash = await sha256Hash(JSON.stringify(registry));
const globalsHash = await sha256Hash(JSON.stringify(globals));

return await sha256Hash(registryHash + globalsHash);
}
}
19 changes: 19 additions & 0 deletions npm/src/globals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { RegistryGlobals } from './globals';
import { JsonGlobals } from './json';

const testGlobals: JsonGlobals = {
rpcs: [{ name: 'rpc1', images: [], url: 'http://rpc1.com' }],
frontends: ['frontend1', 'frontend2', 'frontend3'],
stakingAssetId: {
inner: 'KeqcLzNx9qSH5+lcJHBB9KNW+YPrBk5dKzvPMiypahA=',
},
};

describe('Globals', () => {
it('versions correctly', async () => {
const registry = new RegistryGlobals(testGlobals);
const version = await registry.version();
expect(version).toEqual('7e4059f5641482cfc817222cb5cbd6f62174d578d8473ff4b0a13ef643363b7e');
});
});
24 changes: 16 additions & 8 deletions npm/src/globals.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { Rpc } from './registry';
import { AssetId } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { JsonGlobals } from './json';
import { sha256Hash } from './utils/sha256';

export interface RegistryGlobals {
rpcs: Rpc[];
frontends: string[];
stakingAssetId: AssetId;
export class RegistryGlobals {
readonly stakingAssetId: AssetId;
readonly rpcs: Rpc[];
readonly frontends: string[];

constructor(json: JsonGlobals) {
this.rpcs = json.rpcs;
this.frontends = json.frontends;
this.stakingAssetId = AssetId.fromJson(json.stakingAssetId);
}

async version(): Promise<string> {
return sha256Hash(JSON.stringify(this));
}
}

export const globalsFromJson = (json: JsonGlobals): RegistryGlobals => {
return {
...json,
stakingAssetId: AssetId.fromJson(json.stakingAssetId),
};
return new RegistryGlobals(json);
};
81 changes: 9 additions & 72 deletions npm/src/registry.test.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,8 @@
import { describe, expect, it } from 'vitest';
import { Registry } from './registry';
import { AssetId } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { JsonRegistry } from './json';
import { base64ToUint8Array } from './utils/base64';

const testRegistry: JsonRegistry = {
chainId: 'penumbra-testnet-deimos-6',
ibcConnections: [
{
addressPrefix: 'osmo',
chainId: 'osmo-test-5',
channelId: 'channel-4',
counterpartyChannelId: 'channel-7780',
displayName: 'Osmosis',
images: [
{
svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/f1348793beb994c6cc0256ed7ebdb48c7aa70003/osmosis/images/osmo.svg',
},
],
},
],
assetById: {
'KeqcLzNx9qSH5+lcJHBB9KNW+YPrBk5dKzvPMiypahA=': {
denomUnits: [
{
denom: 'penumbra',
exponent: 6,
},
{
denom: 'mpenumbra',
exponent: 3,
},
{
denom: 'upenumbra',
},
],
base: 'upenumbra',
display: 'penumbra',
symbol: 'UM',
penumbraAssetId: {
inner: 'KeqcLzNx9qSH5+lcJHBB9KNW+YPrBk5dKzvPMiypahA=',
},
images: [
{
svg: 'https://raw.githubusercontent.com/prax-wallet/registry/main/images/um.svg',
},
],
},
'reum7wQmk/owgvGMWMZn/6RFPV24zIKq3W6In/WwZgg=': {
denomUnits: [
{
denom: 'test_usd',
exponent: 18,
},
{
denom: 'wtest_usd',
},
],
base: 'wtest_usd',
display: 'test_usd',
symbol: 'TestUSD',
penumbraAssetId: {
inner: 'reum7wQmk/owgvGMWMZn/6RFPV24zIKq3W6In/WwZgg=',
},
images: [
{
svg: 'https://raw.githubusercontent.com/prax-wallet/registry/main/images/test-usd.svg',
},
],
},
},
numeraires: ['reum7wQmk/owgvGMWMZn/6RFPV24zIKq3W6In/WwZgg='],
};
import * as testRegistry from '../../registry/chains/penumbra-testnet-deimos-8.json';

describe('Registry', () => {
it('gets metadata successfully', () => {
Expand All @@ -83,14 +14,20 @@ describe('Registry', () => {

it('throws when searching for metadata that does not exist', () => {
const registry = new Registry(testRegistry);
const cubeId = base64ToUint8Array('6KBVsPINa8gWSHhfH+kAFJC4afEJA3EtuB2HyCqJUws=');
const cubeId = base64ToUint8Array('aGVsbG8gd29ybGQ=');
const getCubeMetadata = () => registry.getMetadata(new AssetId({ inner: cubeId }));
expect(getCubeMetadata).toThrow();
});

it('gets all assets successfully', () => {
const registry = new Registry(testRegistry);
const res = registry.getAllAssets();
expect(res.length).toEqual(2);
expect(res.length).toEqual(20);
});

it('versions correctly', async () => {
const registry = new Registry(testRegistry);
const version = await registry.version();
expect(version).toEqual('9eaf48c7cbf3248e6979830cfc982f2208eeec0fcc4c0e2802f0bd43c8bffad3');
});
});
5 changes: 5 additions & 0 deletions npm/src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { JsonRegistry } from './json';
import { JsonValue } from '@bufbuild/protobuf';
import { base64ToUint8Array, uint8ArrayToBase64 } from './utils/base64';
import { mapObjectValues } from './utils/object-mapping';
import { sha256Hash } from './utils/sha256';

// @ts-expect-error alias for dev only
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -61,4 +62,8 @@ export class Registry {
getAllAssets(): Metadata[] {
return Object.values(this.assetById);
}

async version(): Promise<string> {
return sha256Hash(JSON.stringify(this));
}
}
99 changes: 10 additions & 89 deletions npm/src/remote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,8 @@ import fetchMock from 'fetch-mock';
import { RemoteClient } from './remote';
import { REGISTRY_BASE_URL } from './github';
import { ChainRegistryClient } from './client';

const mockRegistryResponse = {
chainId: 'penumbra-testnet-deimos-8',
ibcConnections: [
{
addressPrefix: 'osmo',
chainId: 'osmo-test-5',
channelId: 'channel-9',
counterpartyChannelId: 'channel-8343',
displayName: 'Osmosis',
images: [
{
svg: 'https://raw.githubusercontent.com/cosmos/chain-registry/f1348793beb994c6cc0256ed7ebdb48c7aa70003/osmosis/images/osmo.svg',
},
],
},
],
assetById: {
'nDjzm+ldIrNMJha1anGMDVxpA5cLCPnUYQ1clmHF1gw=': {
denomUnits: [
{
denom: 'pizza',
},
],
base: 'pizza',
display: 'pizza',
symbol: 'PIZZA',
penumbraAssetId: {
inner: 'nDjzm+ldIrNMJha1anGMDVxpA5cLCPnUYQ1clmHF1gw=',
},
images: [
{
svg: 'https://raw.githubusercontent.com/prax-wallet/registry/main/images/pizza.svg',
},
],
},
'o2gZdbhCH70Ry+7iBhkSeHC/PB1LZhgkn7LHC2kEhQc=': {
denomUnits: [
{
denom: 'test_btc',
exponent: 8,
},
{
denom: 'test_sat',
},
],
base: 'test_sat',
display: 'test_btc',
symbol: 'TestBTC',
penumbraAssetId: {
inner: 'o2gZdbhCH70Ry+7iBhkSeHC/PB1LZhgkn7LHC2kEhQc=',
},
},
},
numeraires: ['nDjzm+ldIrNMJha1anGMDVxpA5cLCPnUYQ1clmHF1gw='],
};

const mockGlobalsResponse = {
rpcs: ['some-rpc'],
frontends: ['some-frontend'],
stakingAssetId: {
inner: 'KeqcLzNx9qSH5+lcJHBB9KNW+YPrBk5dKzvPMiypahA=',
},
};
import * as Deimos8 from '../../registry/chains/penumbra-testnet-deimos-8.json';
import * as GlobalsJson from '../../registry/globals.json';

describe('RemoteClient', () => {
let rClient: RemoteClient;
Expand All @@ -86,17 +24,15 @@ describe('RemoteClient', () => {
const endpoint = `${REGISTRY_BASE_URL}/chains/${chainId}.json`;
fetchMock.mock(endpoint, {
status: 200,
body: mockRegistryResponse,
body: Deimos8,
});

const registry = await rClient.get(chainId);

expect(fetchMock.called(endpoint)).toBe(true);
expect(registry.chainId).toEqual(mockRegistryResponse.chainId);
expect(registry.ibcConnections).toEqual(mockRegistryResponse.ibcConnections);
expect(registry.getAllAssets().length).toEqual(
Object.keys(mockRegistryResponse.assetById).length,
);
expect(registry.chainId).toEqual(Deimos8.chainId);
expect(registry.ibcConnections).toEqual(Deimos8.ibcConnections);
expect(registry.getAllAssets().length).toEqual(Object.keys(Deimos8.assetById).length);
});

it('should throw if there is not a remote version', async () => {
Expand All @@ -112,29 +48,14 @@ describe('RemoteClient', () => {
const endpoint = `${REGISTRY_BASE_URL}/globals.json`;
fetchMock.mock(endpoint, {
status: 200,
body: mockGlobalsResponse,
body: GlobalsJson,
});

const registry = await rClient.globals();

expect(fetchMock.called(endpoint)).toBe(true);
expect(registry.stakingAssetId.toJson()).toEqual(mockGlobalsResponse.stakingAssetId);
expect(registry.frontends).toEqual(mockGlobalsResponse.frontends);
expect(registry.rpcs).toEqual(mockGlobalsResponse.rpcs);
});

it('should calculate version hash correctly', async () => {
const chainId = 'test-chain-7';
fetchMock.mock(`${REGISTRY_BASE_URL}/chains/${chainId}.json`, {
status: 200,
body: mockRegistryResponse,
});
fetchMock.mock(`${REGISTRY_BASE_URL}/globals.json`, {
status: 200,
body: mockGlobalsResponse,
});

const version = await rClient.version(chainId);
expect(version).toBe('08150a0b5f4dffbbb0a794ad3f6c58d36dc571ce77359c9c7eec3d34cb7a2b15');
expect(registry.stakingAssetId.toJson()).toEqual(GlobalsJson.stakingAssetId);
expect(registry.frontends).toEqual(GlobalsJson.frontends);
expect(registry.rpcs).toEqual(GlobalsJson.rpcs);
});
});
14 changes: 0 additions & 14 deletions npm/src/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Registry } from './registry';
import { GithubFetcher } from './github';
import { swapIfPreviewChain } from './preview';
import { RegistryGlobals } from './globals';
import { sha256Hash } from './utils/sha256';

export class RemoteClient {
private readonly github = new GithubFetcher();
Expand All @@ -12,20 +11,7 @@ export class RemoteClient {
return this.github.fetchRegistry(chainIdToQuery);
}

clearCache() {
this.github.clearCache();
}

async globals(): Promise<RegistryGlobals> {
return this.github.fetchGlobals();
}

async version(chainId: string) {
const registry = await this.get(chainId);
const globals = await this.globals();
const registryHash = await sha256Hash(JSON.stringify(registry));
const globalsHash = await sha256Hash(JSON.stringify(globals));

return await sha256Hash(registryHash + globalsHash);
}
}

0 comments on commit c31e459

Please sign in to comment.