@agoric/casting 0.4.3-dev-648d42f.0 → 0.4.3-dev-dfc712f.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/casting",
|
|
3
|
-
"version": "0.4.3-dev-
|
|
3
|
+
"version": "0.4.3-dev-dfc712f.0+dfc712f",
|
|
4
4
|
"description": "Agoric's OCap broadcasting system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"author": "Agoric",
|
|
23
23
|
"license": "Apache-2.0",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agoric/internal": "0.3.3-dev-
|
|
26
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
27
|
-
"@agoric/spawner": "0.6.9-dev-
|
|
28
|
-
"@agoric/store": "0.9.3-dev-
|
|
25
|
+
"@agoric/internal": "0.3.3-dev-dfc712f.0+dfc712f",
|
|
26
|
+
"@agoric/notifier": "0.6.3-dev-dfc712f.0+dfc712f",
|
|
27
|
+
"@agoric/spawner": "0.6.9-dev-dfc712f.0+dfc712f",
|
|
28
|
+
"@agoric/store": "0.9.3-dev-dfc712f.0+dfc712f",
|
|
29
29
|
"@cosmjs/encoding": "^0.30.1",
|
|
30
30
|
"@cosmjs/proto-signing": "^0.30.1",
|
|
31
31
|
"@cosmjs/stargate": "^0.30.1",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"node-fetch": "^2.6.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@agoric/cosmic-proto": "0.3.1-dev-dfc712f.0+dfc712f",
|
|
41
42
|
"@endo/ses-ava": "^0.2.40",
|
|
42
43
|
"@types/node-fetch": "^2.6.2",
|
|
43
44
|
"ava": "^5.3.0",
|
|
@@ -58,5 +59,5 @@
|
|
|
58
59
|
"timeout": "20m",
|
|
59
60
|
"workerThreads": false
|
|
60
61
|
},
|
|
61
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "dfc712fe1fe243625d24b472acb72cad32c24b17"
|
|
62
63
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export function makeHttpClient(url: string, fetch: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) & ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>)): import('@cosmjs/tendermint-rpc').RpcClient;
|
|
2
|
+
//# sourceMappingURL=makeHttpClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"makeHttpClient.d.ts","sourceRoot":"","sources":["makeHttpClient.js"],"names":[],"mappings":"AA2BO,oCAJI,MAAM,iLAEJ,OAAO,wBAAwB,EAAE,SAAS,CA+BtD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
const { freeze } = Object;
|
|
4
|
+
|
|
5
|
+
const filterBadStatus = res => {
|
|
6
|
+
if (res.status >= 400) {
|
|
7
|
+
throw new Error(`Bad status on response: ${res.status}`);
|
|
8
|
+
}
|
|
9
|
+
return res;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Make an RpcClient using explicit access to the network.
|
|
14
|
+
*
|
|
15
|
+
* The RpcClient implementations included in cosmjs
|
|
16
|
+
* such as {@link https://cosmos.github.io/cosmjs/latest/tendermint-rpc/classes/HttpClient.html HttpClient}
|
|
17
|
+
* use ambient authority (fetch or axios) for network access.
|
|
18
|
+
*
|
|
19
|
+
* To facilitate cooperation without vulnerability,
|
|
20
|
+
* as well as unit testing, etc. this RpcClient maker takes
|
|
21
|
+
* network access as a parameter, following
|
|
22
|
+
* {@link https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline|OCap Discipline}.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} url
|
|
25
|
+
* @param {typeof window.fetch} fetch
|
|
26
|
+
* @returns {import('@cosmjs/tendermint-rpc').RpcClient}
|
|
27
|
+
*/
|
|
28
|
+
export const makeHttpClient = (url, fetch) => {
|
|
29
|
+
const headers = {}; // XXX needed?
|
|
30
|
+
|
|
31
|
+
// based on cosmjs 0.30.1:
|
|
32
|
+
// https://github.com/cosmos/cosmjs/blob/33271bc51cdc865cadb647a1b7ab55d873637f39/packages/tendermint-rpc/src/rpcclients/http.ts#L37
|
|
33
|
+
// https://github.com/cosmos/cosmjs/blob/33271bc51cdc865cadb647a1b7ab55d873637f39/packages/tendermint-rpc/src/rpcclients/httpclient.ts#L25
|
|
34
|
+
return freeze({
|
|
35
|
+
disconnect: () => {
|
|
36
|
+
// nothing to be done
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {import('@cosmjs/json-rpc').JsonRpcRequest} request
|
|
41
|
+
*/
|
|
42
|
+
execute: async request => {
|
|
43
|
+
const settings = {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: request ? JSON.stringify(request) : undefined,
|
|
46
|
+
headers: {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
...headers,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return fetch(url, settings)
|
|
53
|
+
.then(filterBadStatus)
|
|
54
|
+
.then(res => res.json());
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const { stringify: jq } = JSON;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file to regenerate
|
|
5
|
+
* 1. set RECORDING=true in test-interpose-net-access.js
|
|
6
|
+
* 2. run: yarn test test/test-test-interpose-net-access.js --update-snapshots
|
|
7
|
+
* 3. for each map in test-test-interpose-net-access.js.md, copy it and
|
|
8
|
+
* 4. replace all occurences of => with : and paste as args to Object.fromEntries()
|
|
9
|
+
* 5. change RECORDING back to false
|
|
10
|
+
*/
|
|
11
|
+
export const web1 = new Map([
|
|
12
|
+
[
|
|
13
|
+
jq([
|
|
14
|
+
'https://emerynet.rpc.agoric.net/',
|
|
15
|
+
{
|
|
16
|
+
method: 'POST',
|
|
17
|
+
body: jq({
|
|
18
|
+
id: 1208387614,
|
|
19
|
+
method: 'no-such-method',
|
|
20
|
+
params: [],
|
|
21
|
+
jsonrpc: '2.0',
|
|
22
|
+
}),
|
|
23
|
+
headers: { 'Content-Type': 'application/json' },
|
|
24
|
+
},
|
|
25
|
+
]),
|
|
26
|
+
{
|
|
27
|
+
error: {
|
|
28
|
+
code: -32601,
|
|
29
|
+
message: 'Method not found',
|
|
30
|
+
},
|
|
31
|
+
id: 1208387614,
|
|
32
|
+
jsonrpc: '2.0',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
jq([
|
|
37
|
+
'https://emerynet.rpc.agoric.net/',
|
|
38
|
+
{
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: jq({
|
|
41
|
+
jsonrpc: '2.0',
|
|
42
|
+
id: 797030719,
|
|
43
|
+
method: 'abci_query',
|
|
44
|
+
params: {
|
|
45
|
+
path: '/cosmos.bank.v1beta1.Query/Balance',
|
|
46
|
+
data: '0a2d61676f726963313430646d6b727a326534326572676a6a37677976656a687a6d6a7a7572767165713832616e67120475697374',
|
|
47
|
+
prove: false,
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
headers: { 'Content-Type': 'application/json' },
|
|
51
|
+
},
|
|
52
|
+
]),
|
|
53
|
+
{
|
|
54
|
+
id: 797030719,
|
|
55
|
+
jsonrpc: '2.0',
|
|
56
|
+
result: {
|
|
57
|
+
response: {
|
|
58
|
+
code: 0,
|
|
59
|
+
codespace: '',
|
|
60
|
+
height: '123985',
|
|
61
|
+
index: '0',
|
|
62
|
+
info: '',
|
|
63
|
+
key: null,
|
|
64
|
+
log: '',
|
|
65
|
+
proofOps: null,
|
|
66
|
+
value: 'ChAKBHVpc3QSCDI1MDUwMDAw',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
export const web2 = new Map([
|
|
74
|
+
[
|
|
75
|
+
jq([
|
|
76
|
+
'https://emerynet.rpc.agoric.net/',
|
|
77
|
+
{
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: jq({
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id: 1757612624,
|
|
82
|
+
method: 'abci_query',
|
|
83
|
+
params: {
|
|
84
|
+
path: '/agoric.vstorage.Query/Children',
|
|
85
|
+
data: '',
|
|
86
|
+
prove: false,
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
headers: { 'Content-Type': 'application/json' },
|
|
90
|
+
},
|
|
91
|
+
]),
|
|
92
|
+
{
|
|
93
|
+
id: 1757612624,
|
|
94
|
+
jsonrpc: '2.0',
|
|
95
|
+
result: {
|
|
96
|
+
response: {
|
|
97
|
+
code: 0,
|
|
98
|
+
codespace: '',
|
|
99
|
+
height: '123985',
|
|
100
|
+
index: '0',
|
|
101
|
+
info: '',
|
|
102
|
+
key: null,
|
|
103
|
+
log: '',
|
|
104
|
+
proofOps: null,
|
|
105
|
+
value:
|
|
106
|
+
'CgxhY3Rpdml0eWhhc2gKCmJlYW5zT3dpbmcKBmVncmVzcwoTaGlnaFByaW9yaXR5U2VuZGVycwoJcHVibGlzaGVkCgpzd2luZ1N0b3Jl',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} str
|
|
115
|
+
* ack: https://stackoverflow.com/a/7616484
|
|
116
|
+
*/
|
|
117
|
+
const hashCode = str => {
|
|
118
|
+
let hash = 0;
|
|
119
|
+
let i;
|
|
120
|
+
let chr;
|
|
121
|
+
if (str.length === 0) return hash;
|
|
122
|
+
for (i = 0; i < str.length; i += 1) {
|
|
123
|
+
chr = str.charCodeAt(i);
|
|
124
|
+
// eslint-disable-next-line no-bitwise
|
|
125
|
+
hash = (hash << 5) - hash + chr;
|
|
126
|
+
// eslint-disable-next-line no-bitwise
|
|
127
|
+
hash |= 0; // Convert to 32bit integer
|
|
128
|
+
}
|
|
129
|
+
return hash;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Normalize JSON RPC request ID
|
|
134
|
+
*
|
|
135
|
+
* tendermint-rpc generates ids using ambient access to Math.random()
|
|
136
|
+
* So we normalize them to a hash of the rest of the JSON.
|
|
137
|
+
*
|
|
138
|
+
* Earlier, we tried a sequence number, but it was non-deterministic
|
|
139
|
+
* with multiple interleaved requests.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} argsKey
|
|
142
|
+
*/
|
|
143
|
+
const normalizeID = argsKey => {
|
|
144
|
+
// arbitrary string unlikely to occur in a request. from `pwgen 16 -1`
|
|
145
|
+
const placeholder = 'Ajaz1chei7ohnguv';
|
|
146
|
+
|
|
147
|
+
const noid = argsKey.replace(/\\"id\\":\d+/, `\\"id\\":${placeholder}`);
|
|
148
|
+
const id = Math.abs(hashCode(noid));
|
|
149
|
+
return noid.replace(placeholder, `${id}`);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Wrap `fetch` to capture JSON RPC IO traffic.
|
|
154
|
+
*
|
|
155
|
+
* @param {typeof window.fetch} fetch
|
|
156
|
+
* returns wraped fetch along with a .web map for use with {@link replayIO}
|
|
157
|
+
*/
|
|
158
|
+
export const captureIO = fetch => {
|
|
159
|
+
const web = new Map();
|
|
160
|
+
/** @type {typeof window.fetch} */
|
|
161
|
+
// @ts-expect-error mock
|
|
162
|
+
const f = async (...args) => {
|
|
163
|
+
const key = normalizeID(JSON.stringify(args));
|
|
164
|
+
const resp = await fetch(...args);
|
|
165
|
+
return {
|
|
166
|
+
json: async () => {
|
|
167
|
+
const data = await resp.json();
|
|
168
|
+
web.set(key, data);
|
|
169
|
+
return data;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
return { fetch: f, web };
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Replay captured JSON RPC IO.
|
|
178
|
+
*
|
|
179
|
+
* @param {Map<string, any>} web map from
|
|
180
|
+
* JSON-stringified fetch args to fetched JSON data.
|
|
181
|
+
*/
|
|
182
|
+
export const replayIO = web => {
|
|
183
|
+
/** @type {typeof window.fetch} */
|
|
184
|
+
// @ts-expect-error mock
|
|
185
|
+
const f = async (...args) => {
|
|
186
|
+
const key = normalizeID(JSON.stringify(args));
|
|
187
|
+
const data = web.get(key);
|
|
188
|
+
if (!data) {
|
|
189
|
+
throw Error(`no data for ${key}`);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
json: async () => data,
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
return f;
|
|
196
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/* global globalThis */
|
|
3
|
+
import anyTest from 'ava';
|
|
4
|
+
import {
|
|
5
|
+
createProtobufRpcClient,
|
|
6
|
+
QueryClient,
|
|
7
|
+
setupBankExtension,
|
|
8
|
+
} from '@cosmjs/stargate';
|
|
9
|
+
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
|
10
|
+
import { QueryClientImpl } from '@agoric/cosmic-proto/vstorage/query.js';
|
|
11
|
+
|
|
12
|
+
import { makeHttpClient } from '../src/makeHttpClient.js';
|
|
13
|
+
import { captureIO, replayIO, web1, web2 } from './net-access-fixture.js';
|
|
14
|
+
|
|
15
|
+
/** @type {import('ava').TestFn<Awaited<ReturnType<typeof makeTestContext>>>} */
|
|
16
|
+
const test = /** @type {any} */ (anyTest);
|
|
17
|
+
|
|
18
|
+
const RECORDING = false;
|
|
19
|
+
|
|
20
|
+
const makeTestContext = async () => {
|
|
21
|
+
return { fetch: globalThis.fetch };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
test.before(async t => {
|
|
25
|
+
t.context = await makeTestContext();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const scenario1 = {
|
|
29
|
+
endpoint: 'https://emerynet.rpc.agoric.net/',
|
|
30
|
+
request: {
|
|
31
|
+
id: 1,
|
|
32
|
+
method: 'no-such-method',
|
|
33
|
+
params: [],
|
|
34
|
+
},
|
|
35
|
+
gov2: {
|
|
36
|
+
addr: 'agoric140dmkrz2e42ergjj7gyvejhzmjzurvqeq82ang',
|
|
37
|
+
balance: { amount: '25050000', denom: 'uist' },
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
test('interpose net access', async t => {
|
|
42
|
+
const fetchMock = replayIO(web1);
|
|
43
|
+
const rpcClient = makeHttpClient(scenario1.endpoint, fetchMock);
|
|
44
|
+
|
|
45
|
+
t.log('raw JSON RPC');
|
|
46
|
+
const res = await rpcClient.execute({
|
|
47
|
+
...scenario1.request,
|
|
48
|
+
jsonrpc: '2.0',
|
|
49
|
+
});
|
|
50
|
+
t.like(res, { error: { message: 'Method not found' } });
|
|
51
|
+
|
|
52
|
+
t.log('Cosmos SDK RPC: balance query');
|
|
53
|
+
const tmClient = await Tendermint34Client.create(rpcClient);
|
|
54
|
+
const qClient = new QueryClient(tmClient);
|
|
55
|
+
const ext = setupBankExtension(qClient);
|
|
56
|
+
const actual = await ext.bank.balance(
|
|
57
|
+
scenario1.gov2.addr,
|
|
58
|
+
scenario1.gov2.balance.denom,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
t.deepEqual(actual, scenario1.gov2.balance);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const scenario2 = {
|
|
65
|
+
endpoint: 'https://emerynet.rpc.agoric.net/',
|
|
66
|
+
children: [
|
|
67
|
+
'activityhash',
|
|
68
|
+
'beansOwing',
|
|
69
|
+
'egress',
|
|
70
|
+
'highPrioritySenders',
|
|
71
|
+
'published',
|
|
72
|
+
'swingStore',
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
test(`vstorage query: Children (RECORDING: ${RECORDING})`, async t => {
|
|
77
|
+
const { context: io } = t;
|
|
78
|
+
|
|
79
|
+
const { fetch: fetchMock, web } = io.recording
|
|
80
|
+
? captureIO(io.fetch)
|
|
81
|
+
: { fetch: replayIO(web2), web: new Map() };
|
|
82
|
+
const rpcClient = makeHttpClient(scenario2.endpoint, fetchMock);
|
|
83
|
+
|
|
84
|
+
const tmClient = await Tendermint34Client.create(rpcClient);
|
|
85
|
+
const qClient = new QueryClient(tmClient);
|
|
86
|
+
const rpc = createProtobufRpcClient(qClient);
|
|
87
|
+
const queryService = new QueryClientImpl(rpc);
|
|
88
|
+
|
|
89
|
+
const children = await queryService.Children({ path: '' });
|
|
90
|
+
if (io.recording) {
|
|
91
|
+
t.snapshot(web);
|
|
92
|
+
}
|
|
93
|
+
t.deepEqual(children, {
|
|
94
|
+
children: scenario2.children,
|
|
95
|
+
pagination: undefined,
|
|
96
|
+
});
|
|
97
|
+
});
|