Skip to content

Commit

Permalink
chore(a3p): update makeVStorage according to batchQuery.js
Browse files Browse the repository at this point in the history
rel: #10574
  • Loading branch information
Jorge-Lopes committed Dec 2, 2024
1 parent d8a109e commit b8c0f7d
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 111 deletions.
46 changes: 46 additions & 0 deletions a3p-integration/proposals/z:acceptance/test-lib/makeHttpClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { assert } from '@endo/errors';
import { Far } from '@endo/far';

/**
* gRPC-gateway REST API access
*
* @see {@link https://docs.cosmos.network/v0.45/core/grpc_rest.html#rest-server Cosmos SDK REST Server}
*
* Note: avoid Legacy REST routes, per
* {@link https://docs.cosmos.network/v0.45/migrations/rest.html Cosmos SDK REST Endpoints Migration}.
*
* @param {string} apiAddress nodes default to port 1317
* @param {object} io
* @param {typeof fetch} io.fetch
*/
export const makeAPI = (apiAddress, { fetch }) => {
assert.typeof(apiAddress, 'string');

/**
* @param {string} href
* @param {object} [options]
* @param {Record<string, string>} [options.headers]
*/
const getJSON = (href, options = {}) => {
const opts = {
keepalive: true,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
};
const url = `${apiAddress}${href}`;
return fetch(url, opts).then(r => {
if (!r.ok) throw Error(r.statusText);
return r.json().then(data => {
return data;
});
});
};

return Far('LCD', {
getJSON,
latestBlock: () => getJSON(`/cosmos/base/tendermint/v1beta1/blocks/latest`),
});
};
/** @typedef {ReturnType<typeof makeAPI>} LCD */
198 changes: 87 additions & 111 deletions a3p-integration/proposals/z:acceptance/test-lib/rpc.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/** @file copied from packages/agoric-cli */
// TODO DRY in https://github.com/Agoric/agoric-sdk/issues/9109
// @ts-check
/* global Buffer */

import {
boardSlottingMarshaller,
makeBoardRemote,
} from '@agoric/internal/src/marshal.js';
import { Fail } from '@endo/errors';
import { E } from '@endo/far';
import { makeAPI } from './makeHttpClient.js';

export { boardSlottingMarshaller };

Expand Down Expand Up @@ -39,123 +40,95 @@ export { networkConfig };
// console.warn('networkConfig', networkConfig);

/**
* @param {object} powers
* @param {typeof window.fetch} powers.fetch
* @param {MinimalNetworkConfig} config
* @template T
* @param {(value: string) => T} f
* @param {AsyncGenerator<string[], void, unknown>} chunks
*/
export const makeVStorage = (powers, config = networkConfig) => {
/** @param {string} path */
const getJSON = path => {
const url = config.rpcAddrs[0] + path;
// console.warn('fetching', url);
return powers.fetch(url, { keepalive: true }).then(res => res.json());
};
async function* mapHistory(f, chunks) {
for await (const chunk of chunks) {
if (chunk === undefined) continue;
for (const value of chunk.reverse()) {
yield f(value);
}
}
}

/**
* @param {ERef<import('./makeHttpClient').LCD>} lcd
*/
export const makeVStorage = lcd => {
const getJSON = (href, options) => E(lcd).getJSON(href, options);

// height=0 is the same as omitting height and implies the highest block
const url = (path = 'published', { kind = 'children', height = 0 } = {}) =>
`/abci_query?path=%22/custom/vstorage/${kind}/${path}%22&height=${height}`;
const href = (path = 'published', { kind = 'data' } = {}) =>
`/agoric/vstorage/${kind}/${path}`;
const headers = height =>
height ? { 'x-cosmos-block-height': `${height}` } : undefined;

const readStorage = (
path = 'published',
{ kind = 'data', height = 0 } = {},
) =>
getJSON(href(path, { kind }), { headers: headers(height) }).catch(err => {
throw Error(`cannot read ${kind} of ${path}: ${err.message}`);
});
const readCell = (path, opts) =>
readStorage(path, opts)
.then(data => data.value)
.then(s => (s === '' ? {} : JSON.parse(s)));

const readStorage = (path = 'published', { kind = 'children', height = 0 }) =>
getJSON(url(path, { kind, height }))
.catch(err => {
throw Error(`cannot read ${kind} of ${path}: ${err.message}`);
})
.then(data => {
const {
result: { response },
} = data;
if (response?.code !== 0) {
/** @type {any} */
const err = Error(
`error code ${response?.code} reading ${kind} of ${path}: ${response.log}`,
);
err.code = response?.code;
err.codespace = response?.codespace;
throw err;
/**
* Read values going back as far as available
*
* @param {string} path
* @param {number | string} [minHeight]
*/
async function* readHistory(path, minHeight = undefined) {
// undefined the first iteration, to query at the highest
let blockHeight;
await null;
do {
// console.debug('READING', { blockHeight });
/** @type {string[]} */
let values = [];
try {
({ blockHeight, values } = await readCell(path, {
kind: 'data',
height: blockHeight && Number(blockHeight) - 1,
}));
// console.debug('readAt returned', { blockHeight });
} catch (err) {
if (err.message.match(/unknown request/)) {
// XXX FIXME
// console.error(err);
break;
}
return data;
});
throw err;
}
yield values;
// console.debug('PUSHED', values);
// console.debug('NEW', { blockHeight, minHeight });
if (minHeight && Number(blockHeight) <= Number(minHeight)) break;
} while (blockHeight > 0);
}

/**
* @template T
* @param {(value: string) => T} f
* @param {string} path
* @param {number | string} [minHeight]
*/
const readHistoryBy = (f, path, minHeight) =>
mapHistory(f, readHistory(path, minHeight));

return {
url,
/** @param {{ result: { response: { code: number, value: string } } }} rawResponse */
decode({ result: { response } }) {
const { code } = response;
if (code !== 0) {
throw response;
}
const { value } = response;
return Buffer.from(value, 'base64').toString();
},
/**
*
* @param {string} path
* @returns {Promise<string>} latest vstorage value at path
*/
async readLatest(path = 'published') {
const raw = await readStorage(path, { kind: 'data' });
return this.decode(raw);
},
async keys(path = 'published') {
const raw = await readStorage(path, { kind: 'children' });
return JSON.parse(this.decode(raw)).children;
},
/**
* @param {string} path
* @param {number} [height] default is highest
* @returns {Promise<{blockHeight: number, values: string[]}>}
*/
async readAt(path, height = undefined) {
const raw = await readStorage(path, { kind: 'data', height });
const txt = this.decode(raw);
/** @type {{ value: string }} */
const { value } = JSON.parse(txt);
return JSON.parse(value);
},
/**
* Read values going back as far as available
*
* @param {string} path
* @param {number | string} [minHeight]
* @returns {Promise<string[]>}
*/
async readFully(path, minHeight = undefined) {
const parts = [];
// undefined the first iteration, to query at the highest
let blockHeight;
await null;
do {
// console.debug('READING', { blockHeight });
let values;
try {
({ blockHeight, values } = await this.readAt(
path,
blockHeight && Number(blockHeight) - 1,
));
// console.debug('readAt returned', { blockHeight });
} catch (err) {
if (
// CosmosSDK ErrNotFound; there is no data at the path
(err.codespace === 'sdk' && err.code === 38) ||
// CosmosSDK ErrUnknownRequest; misrepresentation of the same until
// https://github.com/Agoric/agoric-sdk/commit/dafc7c1708977aaa55e245dc09a73859cf1df192
// TODO remove after upgrade-12
err.message.match(/unknown request/)
) {
// console.error(err);
break;
}
throw err;
}
parts.push(values);
// console.debug('PUSHED', values);
// console.debug('NEW', { blockHeight, minHeight });
if (minHeight && Number(blockHeight) <= Number(minHeight)) break;
} while (blockHeight > 0);
return parts.flat();
},
lcd,
readStorage,
readLatest: readCell,
readHistory,
readHistoryBy,
};
};
/** @typedef {ReturnType<typeof makeVStorage>} VStorage */

export const makeFromBoard = () => {
const cache = new Map();
Expand Down Expand Up @@ -243,7 +216,10 @@ export const makeAgoricNames = async (ctx, vstorage) => {
export const makeVstorageKit = async ({ fetch }, config = networkConfig) => {
await null;
try {
const vstorage = makeVStorage({ fetch }, config);
const apiAddress = 'http://0.0.0.0:1317';
const lcd = makeAPI(apiAddress, { fetch });
const vstorage = makeVStorage(lcd);

const fromBoard = makeFromBoard();
const agoricNames = await makeAgoricNames(fromBoard, vstorage);

Expand Down

0 comments on commit b8c0f7d

Please sign in to comment.