@agoric/orchestration 0.1.1-dev-a477bee.0 → 0.1.1-dev-2b7439e.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/index.js +2 -0
- package/package.json +14 -10
- package/src/contracts/stakeAtom.contract.js +52 -0
- package/src/orchestration.js +240 -0
- package/src/proposals/orchestration-proposal.js +99 -0
- package/src/proposals/start-stakeAtom.js +68 -0
- package/src/types.d.ts +8 -0
- package/src/types.js +1 -0
- package/src/utils/address.js +54 -0
- package/src/vat-orchestration.js +22 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/orchestration",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-2b7439e.0+2b7439e",
|
|
4
4
|
"description": "Chain abstraction for Agoric's orchestration clients",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -29,13 +29,16 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@agoric/
|
|
33
|
-
"@agoric/
|
|
34
|
-
"@agoric/
|
|
35
|
-
"@agoric/
|
|
36
|
-
"@agoric/
|
|
37
|
-
"@agoric/
|
|
38
|
-
"@agoric/
|
|
32
|
+
"@agoric/assert": "0.6.1-dev-2b7439e.0+2b7439e",
|
|
33
|
+
"@agoric/ertp": "0.16.3-dev-2b7439e.0+2b7439e",
|
|
34
|
+
"@agoric/internal": "0.3.3-dev-2b7439e.0+2b7439e",
|
|
35
|
+
"@agoric/network": "0.1.1-dev-2b7439e.0+2b7439e",
|
|
36
|
+
"@agoric/notifier": "0.6.3-dev-2b7439e.0+2b7439e",
|
|
37
|
+
"@agoric/store": "0.9.3-dev-2b7439e.0+2b7439e",
|
|
38
|
+
"@agoric/vat-data": "0.5.3-dev-2b7439e.0+2b7439e",
|
|
39
|
+
"@agoric/vats": "0.15.2-dev-2b7439e.0+2b7439e",
|
|
40
|
+
"@agoric/zoe": "0.26.3-dev-2b7439e.0+2b7439e",
|
|
41
|
+
"@agoric/zone": "0.2.3-dev-2b7439e.0+2b7439e",
|
|
39
42
|
"@endo/far": "^1.1.0",
|
|
40
43
|
"@endo/marshal": "^1.4.0",
|
|
41
44
|
"@endo/patterns": "^1.3.0"
|
|
@@ -43,7 +46,8 @@
|
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@cosmjs/amino": "^0.32.3",
|
|
45
48
|
"@cosmjs/proto-signing": "^0.32.3",
|
|
46
|
-
"ava": "^
|
|
49
|
+
"@endo/ses-ava": "^1.2.0",
|
|
50
|
+
"ava": "^5.3.1",
|
|
47
51
|
"cosmjs-types": "^0.9.0"
|
|
48
52
|
},
|
|
49
53
|
"ava": {
|
|
@@ -76,5 +80,5 @@
|
|
|
76
80
|
"typeCoverage": {
|
|
77
81
|
"atLeast": 96.96
|
|
78
82
|
},
|
|
79
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "2b7439e3ad174ead924c942b105a4ad348d2bea9"
|
|
80
84
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Example contract that uses orchestration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
7
|
+
import { V as E } from '@agoric/vat-data/vow.js';
|
|
8
|
+
import { M } from '@endo/patterns';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @import * as orchestration from '../types'
|
|
12
|
+
* @import * as vatData from '@agoric/vat-data'
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {{
|
|
17
|
+
* hostConnectionId: orchestration.ConnectionId;
|
|
18
|
+
* controllerConnectionId: orchestration.ConnectionId;
|
|
19
|
+
* }} StakeAtomTerms
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param {ZCF<StakeAtomTerms>} zcf
|
|
25
|
+
* @param {{
|
|
26
|
+
* orchestration: orchestration.Orchestration;
|
|
27
|
+
* }} privateArgs
|
|
28
|
+
* @param {vatData.Baggage} baggage
|
|
29
|
+
*/
|
|
30
|
+
export const start = async (zcf, privateArgs, baggage) => {
|
|
31
|
+
const { hostConnectionId, controllerConnectionId } = zcf.getTerms();
|
|
32
|
+
const { orchestration } = privateArgs;
|
|
33
|
+
|
|
34
|
+
const zone = makeDurableZone(baggage);
|
|
35
|
+
|
|
36
|
+
const publicFacet = zone.exo(
|
|
37
|
+
'StakeAtom',
|
|
38
|
+
M.interface('StakeAtomI', {
|
|
39
|
+
createAccount: M.callWhen().returns(M.remotable('ChainAccount')),
|
|
40
|
+
}),
|
|
41
|
+
{
|
|
42
|
+
async createAccount() {
|
|
43
|
+
return E(orchestration).createAccount(
|
|
44
|
+
hostConnectionId,
|
|
45
|
+
controllerConnectionId,
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return { publicFacet };
|
|
52
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @file Orchestration service */
|
|
3
|
+
import { NonNullish } from '@agoric/assert';
|
|
4
|
+
import { makeTracer } from '@agoric/internal';
|
|
5
|
+
import { V as E } from '@agoric/vat-data/vow.js';
|
|
6
|
+
import { M } from '@endo/patterns';
|
|
7
|
+
import { makeICAConnectionAddress, parseAddress } from './utils/address.js';
|
|
8
|
+
import '@agoric/network/exported.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @import { ConnectionId } from './types';
|
|
12
|
+
* @import { Zone } from '@agoric/base-zone';
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { Fail, bare } = assert;
|
|
16
|
+
const trace = makeTracer('Orchestration');
|
|
17
|
+
|
|
18
|
+
// TODO improve me
|
|
19
|
+
/** @typedef {string} ChainAddress */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} OrchestrationPowers
|
|
23
|
+
* @property {ERef<
|
|
24
|
+
* import('@agoric/orchestration/src/types').AttenuatedNetwork
|
|
25
|
+
* >} network
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* PowerStore is used so additional powers can be added on upgrade. See
|
|
30
|
+
* [#7337](https://github.com/Agoric/agoric-sdk/issues/7337) for tracking on Exo
|
|
31
|
+
* state migrations.
|
|
32
|
+
*
|
|
33
|
+
* @typedef {MapStore<
|
|
34
|
+
* keyof OrchestrationPowers,
|
|
35
|
+
* OrchestrationPowers[keyof OrchestrationPowers]
|
|
36
|
+
* >} PowerStore
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @template {keyof OrchestrationPowers} K
|
|
41
|
+
* @param {PowerStore} powers
|
|
42
|
+
* @param {K} name
|
|
43
|
+
*/
|
|
44
|
+
const getPower = (powers, name) => {
|
|
45
|
+
powers.has(name) || Fail`need powers.${bare(name)} for this method`;
|
|
46
|
+
return /** @type {OrchestrationPowers[K]} */ (powers.get(name));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const ChainAccountI = M.interface('ChainAccount', {
|
|
50
|
+
getAccountAddress: M.call().returns(M.string()),
|
|
51
|
+
getLocalAddress: M.call().returns(M.string()),
|
|
52
|
+
getRemoteAddress: M.call().returns(M.string()),
|
|
53
|
+
getPort: M.call().returns(M.remotable('Port')),
|
|
54
|
+
close: M.callWhen().returns(M.string()),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const ConnectionHandlerI = M.interface('ConnectionHandler', {
|
|
58
|
+
onOpen: M.callWhen(M.any(), M.string(), M.string(), M.any()).returns(M.any()),
|
|
59
|
+
onClose: M.callWhen(M.any(), M.any(), M.any()).returns(M.any()),
|
|
60
|
+
onReceive: M.callWhen(M.any(), M.string()).returns(M.any()),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/** @param {Zone} zone */
|
|
64
|
+
const prepareChainAccount = zone =>
|
|
65
|
+
zone.exoClassKit(
|
|
66
|
+
'ChainAccount',
|
|
67
|
+
{ account: ChainAccountI, connectionHandler: ConnectionHandlerI },
|
|
68
|
+
/**
|
|
69
|
+
* @param {Port} port
|
|
70
|
+
* @param {string} requestedRemoteAddress
|
|
71
|
+
*/
|
|
72
|
+
(port, requestedRemoteAddress) =>
|
|
73
|
+
/**
|
|
74
|
+
* @type {{
|
|
75
|
+
* port: Port;
|
|
76
|
+
* connection: Connection | undefined;
|
|
77
|
+
* localAddress: string | undefined;
|
|
78
|
+
* requestedRemoteAddress: string;
|
|
79
|
+
* remoteAddress: string | undefined;
|
|
80
|
+
* accountAddress: ChainAddress | undefined;
|
|
81
|
+
* }}
|
|
82
|
+
*/ (
|
|
83
|
+
harden({
|
|
84
|
+
port,
|
|
85
|
+
connection: undefined,
|
|
86
|
+
requestedRemoteAddress,
|
|
87
|
+
remoteAddress: undefined,
|
|
88
|
+
accountAddress: undefined,
|
|
89
|
+
localAddress: undefined,
|
|
90
|
+
})
|
|
91
|
+
),
|
|
92
|
+
{
|
|
93
|
+
account: {
|
|
94
|
+
getAccountAddress() {
|
|
95
|
+
return NonNullish(
|
|
96
|
+
this.state.accountAddress,
|
|
97
|
+
'Error parsing account address from remote address',
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
getLocalAddress() {
|
|
101
|
+
return NonNullish(
|
|
102
|
+
this.state.localAddress,
|
|
103
|
+
'local address not available',
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
getRemoteAddress() {
|
|
107
|
+
return NonNullish(
|
|
108
|
+
this.state.remoteAddress,
|
|
109
|
+
'remote address not available',
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
getPort() {
|
|
113
|
+
return this.state.port;
|
|
114
|
+
},
|
|
115
|
+
async close() {
|
|
116
|
+
/// XXX what should the behavior be here? and `onClose`?
|
|
117
|
+
// - retrieve assets?
|
|
118
|
+
// - revoke the port?
|
|
119
|
+
const { connection } = this.state;
|
|
120
|
+
if (!connection) throw Fail`connection not available`;
|
|
121
|
+
await null;
|
|
122
|
+
try {
|
|
123
|
+
await E(connection).close();
|
|
124
|
+
} catch (e) {
|
|
125
|
+
throw Fail`Failed to close connection: ${e}`;
|
|
126
|
+
}
|
|
127
|
+
return 'Connection closed';
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
connectionHandler: {
|
|
131
|
+
/**
|
|
132
|
+
* @param {Connection} connection
|
|
133
|
+
* @param {string} localAddr
|
|
134
|
+
* @param {string} remoteAddr
|
|
135
|
+
*/
|
|
136
|
+
async onOpen(connection, localAddr, remoteAddr) {
|
|
137
|
+
trace(`ICA Channel Opened for ${localAddr} at ${remoteAddr}`);
|
|
138
|
+
this.state.connection = connection;
|
|
139
|
+
this.state.remoteAddress = remoteAddr;
|
|
140
|
+
this.state.localAddress = localAddr;
|
|
141
|
+
// XXX parseAddress currently throws, should it return '' instead?
|
|
142
|
+
this.state.accountAddress = parseAddress(remoteAddr);
|
|
143
|
+
},
|
|
144
|
+
async onClose(_connection, reason) {
|
|
145
|
+
trace(`ICA Channel closed. Reason: ${reason}`);
|
|
146
|
+
// XXX handle connection closing
|
|
147
|
+
// XXX is there a scenario where a connection will unexpectedly close? _I think yes_
|
|
148
|
+
},
|
|
149
|
+
async onReceive(connection, bytes) {
|
|
150
|
+
trace(`ICA Channel onReceive`, connection, bytes);
|
|
151
|
+
return '';
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
export const OrchestrationI = M.interface('Orchestration', {
|
|
158
|
+
createAccount: M.callWhen(M.string(), M.string()).returns(
|
|
159
|
+
M.remotable('ChainAccount'),
|
|
160
|
+
),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {Zone} zone
|
|
165
|
+
* @param {ReturnType<typeof prepareChainAccount>} createChainAccount
|
|
166
|
+
*/
|
|
167
|
+
const prepareOrchestration = (zone, createChainAccount) =>
|
|
168
|
+
zone.exoClassKit(
|
|
169
|
+
'Orchestration',
|
|
170
|
+
{
|
|
171
|
+
self: M.interface('OrchestrationSelf', {
|
|
172
|
+
bindPort: M.callWhen().returns(M.remotable()),
|
|
173
|
+
}),
|
|
174
|
+
public: OrchestrationI,
|
|
175
|
+
},
|
|
176
|
+
/** @param {Partial<OrchestrationPowers>} [initialPowers] */
|
|
177
|
+
initialPowers => {
|
|
178
|
+
/** @type {PowerStore} */
|
|
179
|
+
const powers = zone.detached().mapStore('PowerStore');
|
|
180
|
+
if (initialPowers) {
|
|
181
|
+
for (const [name, power] of Object.entries(initialPowers)) {
|
|
182
|
+
powers.init(/** @type {keyof OrchestrationPowers} */ (name), power);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { powers, icaControllerNonce: 0 };
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
self: {
|
|
189
|
+
async bindPort() {
|
|
190
|
+
const network = getPower(this.state.powers, 'network');
|
|
191
|
+
const port = await E(network)
|
|
192
|
+
.bind(`/ibc-port/icacontroller-${this.state.icaControllerNonce}`)
|
|
193
|
+
.catch(e => Fail`Failed to bind port: ${e}`);
|
|
194
|
+
this.state.icaControllerNonce += 1;
|
|
195
|
+
return port;
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
public: {
|
|
199
|
+
/**
|
|
200
|
+
* @param {ConnectionId} hostConnectionId
|
|
201
|
+
* the counterparty connection_id
|
|
202
|
+
* @param {ConnectionId} controllerConnectionId
|
|
203
|
+
* self connection_id
|
|
204
|
+
* @returns {Promise<ChainAccount>}
|
|
205
|
+
*/
|
|
206
|
+
async createAccount(hostConnectionId, controllerConnectionId) {
|
|
207
|
+
const port = await this.facets.self.bindPort();
|
|
208
|
+
|
|
209
|
+
const remoteConnAddr = makeICAConnectionAddress(
|
|
210
|
+
hostConnectionId,
|
|
211
|
+
controllerConnectionId,
|
|
212
|
+
);
|
|
213
|
+
const chainAccount = createChainAccount(port, remoteConnAddr);
|
|
214
|
+
|
|
215
|
+
// await so we do not return a ChainAccount before it successfully instantiates
|
|
216
|
+
await E(port)
|
|
217
|
+
.connect(remoteConnAddr, chainAccount.connectionHandler)
|
|
218
|
+
// XXX if we fail, should we close the port (if it was created in this flow)?
|
|
219
|
+
.catch(e => Fail`Failed to create ICA connection: ${bare(e)}`);
|
|
220
|
+
|
|
221
|
+
return chainAccount.account;
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
/** @param {Zone} zone */
|
|
228
|
+
export const prepareOrchestrationTools = zone => {
|
|
229
|
+
const createChainAccount = prepareChainAccount(zone);
|
|
230
|
+
const makeOrchestration = prepareOrchestration(zone, createChainAccount);
|
|
231
|
+
|
|
232
|
+
return harden({ makeOrchestration });
|
|
233
|
+
};
|
|
234
|
+
harden(prepareOrchestrationTools);
|
|
235
|
+
|
|
236
|
+
/** @typedef {ReturnType<ReturnType<typeof prepareChainAccount>>} ChainAccountKit */
|
|
237
|
+
/** @typedef {ChainAccountKit['account']} ChainAccount */
|
|
238
|
+
/** @typedef {ReturnType<typeof prepareOrchestrationTools>} OrchestrationTools */
|
|
239
|
+
/** @typedef {ReturnType<OrchestrationTools['makeOrchestration']>} OrchestrationKit */
|
|
240
|
+
/** @typedef {OrchestrationKit['public']} Orchestration */
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { V as E } from '@agoric/vat-data/vow.js';
|
|
3
|
+
import { Far } from '@endo/far';
|
|
4
|
+
|
|
5
|
+
/** @import { AttenuatedNetwork, Orchestration, OrchestrationVat } from '../types' */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {BootstrapPowers & {
|
|
9
|
+
* consume: {
|
|
10
|
+
* loadCriticalVat: VatLoader<any>;
|
|
11
|
+
* networkVat: NetworkVat;
|
|
12
|
+
* };
|
|
13
|
+
* produce: {
|
|
14
|
+
* orchestration: Producer<any>;
|
|
15
|
+
* orchestrationKit: Producer<any>;
|
|
16
|
+
* orchestrationVat: Producer<any>;
|
|
17
|
+
* };
|
|
18
|
+
* }} powers
|
|
19
|
+
* @param {object} options
|
|
20
|
+
* @param {{ orchestrationRef: VatSourceRef }} options.options
|
|
21
|
+
*
|
|
22
|
+
* @typedef {{
|
|
23
|
+
* orchestration: ERef<OrchestrationVat>;
|
|
24
|
+
* }} OrchestrationVats
|
|
25
|
+
*/
|
|
26
|
+
export const setupOrchestrationVat = async (
|
|
27
|
+
{
|
|
28
|
+
consume: { loadCriticalVat, networkVat },
|
|
29
|
+
produce: {
|
|
30
|
+
orchestrationVat,
|
|
31
|
+
orchestration,
|
|
32
|
+
orchestrationKit: orchestrationKitP,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
options,
|
|
36
|
+
) => {
|
|
37
|
+
const { orchestrationRef } = options.options;
|
|
38
|
+
/** @type {OrchestrationVats} */
|
|
39
|
+
const vats = {
|
|
40
|
+
orchestration: E(loadCriticalVat)('orchestration', orchestrationRef),
|
|
41
|
+
};
|
|
42
|
+
// don't proceed if loadCriticalVat fails
|
|
43
|
+
await Promise.all(Object.values(vats));
|
|
44
|
+
|
|
45
|
+
orchestrationVat.reset();
|
|
46
|
+
orchestrationVat.resolve(vats.orchestration);
|
|
47
|
+
|
|
48
|
+
await networkVat;
|
|
49
|
+
/** @type {AttenuatedNetwork} */
|
|
50
|
+
const network = Far('Attenuated Network', {
|
|
51
|
+
/** @param {string} localAddr */
|
|
52
|
+
async bind(localAddr) {
|
|
53
|
+
return E(networkVat).bind(localAddr);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const newOrchestrationKit = await E(vats.orchestration).makeOrchestration({
|
|
58
|
+
network,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
orchestration.reset();
|
|
62
|
+
orchestration.resolve(newOrchestrationKit.public);
|
|
63
|
+
orchestrationKitP.reset();
|
|
64
|
+
orchestrationKitP.resolve(newOrchestrationKit);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {BootstrapPowers & {
|
|
69
|
+
* consume: {
|
|
70
|
+
* orchestration: Orchestration;
|
|
71
|
+
* };
|
|
72
|
+
* }} powers
|
|
73
|
+
* @param {object} _options
|
|
74
|
+
*/
|
|
75
|
+
export const addOrchestrationToClient = async (
|
|
76
|
+
{ consume: { client, orchestration } },
|
|
77
|
+
_options,
|
|
78
|
+
) => {
|
|
79
|
+
return E(client).assignBundle([_a => ({ orchestration })]);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const getManifestForOrchestration = (_powers, { orchestrationRef }) => ({
|
|
83
|
+
manifest: {
|
|
84
|
+
[setupOrchestrationVat.name]: {
|
|
85
|
+
consume: {
|
|
86
|
+
loadCriticalVat: true,
|
|
87
|
+
networkVat: true,
|
|
88
|
+
},
|
|
89
|
+
produce: {
|
|
90
|
+
orchestration: 'orchestration',
|
|
91
|
+
orchestrationKit: 'orchestration',
|
|
92
|
+
orchestrationVat: 'orchestration',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
options: {
|
|
97
|
+
orchestrationRef,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { makeTracer } from '@agoric/internal';
|
|
3
|
+
import { E } from '@endo/far';
|
|
4
|
+
|
|
5
|
+
const trace = makeTracer('StartStakeAtom', true);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {BootstrapPowers & { installation: {consume: {stakeAtom: Installation<import('../contracts/stakeAtom.contract.js').start>}}}} powers
|
|
9
|
+
* @param {{options: import('../contracts/stakeAtom.contract.js').StakeAtomTerms}} options
|
|
10
|
+
*/
|
|
11
|
+
export const startStakeAtom = async (
|
|
12
|
+
{
|
|
13
|
+
consume: { orchestration, startUpgradable },
|
|
14
|
+
installation: {
|
|
15
|
+
consume: { stakeAtom },
|
|
16
|
+
},
|
|
17
|
+
instance: {
|
|
18
|
+
produce: { stakeAtom: produceInstance },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{ options: { hostConnectionId, controllerConnectionId } },
|
|
22
|
+
) => {
|
|
23
|
+
trace('startStakeAtom', { hostConnectionId, controllerConnectionId });
|
|
24
|
+
await null;
|
|
25
|
+
|
|
26
|
+
/** @type {StartUpgradableOpts<import('../contracts/stakeAtom.contract.js').start>} */
|
|
27
|
+
const startOpts = {
|
|
28
|
+
label: 'stakeAtom',
|
|
29
|
+
installation: stakeAtom,
|
|
30
|
+
terms: {
|
|
31
|
+
hostConnectionId,
|
|
32
|
+
controllerConnectionId,
|
|
33
|
+
},
|
|
34
|
+
privateArgs: {
|
|
35
|
+
orchestration: await orchestration,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const { instance } = await E(startUpgradable)(startOpts);
|
|
40
|
+
produceInstance.resolve(instance);
|
|
41
|
+
};
|
|
42
|
+
harden(startStakeAtom);
|
|
43
|
+
|
|
44
|
+
export const getManifestForStakeAtom = (
|
|
45
|
+
{ restoreRef },
|
|
46
|
+
{ installKeys, ...options },
|
|
47
|
+
) => {
|
|
48
|
+
return {
|
|
49
|
+
manifest: {
|
|
50
|
+
[startStakeAtom.name]: {
|
|
51
|
+
consume: {
|
|
52
|
+
orchestration: true,
|
|
53
|
+
startUpgradable: true,
|
|
54
|
+
},
|
|
55
|
+
installation: {
|
|
56
|
+
consume: { stakeAtom: true },
|
|
57
|
+
},
|
|
58
|
+
instance: {
|
|
59
|
+
produce: { stakeAtom: true },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
installations: {
|
|
64
|
+
stakeAtom: restoreRef(installKeys.stakeAtom),
|
|
65
|
+
},
|
|
66
|
+
options,
|
|
67
|
+
};
|
|
68
|
+
};
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RouterProtocol } from '@agoric/network/src/router';
|
|
2
|
+
|
|
3
|
+
export type ConnectionId = `connection-${number}`;
|
|
4
|
+
|
|
5
|
+
export type AttenuatedNetwork = Pick<RouterProtocol, 'bind'>;
|
|
6
|
+
|
|
7
|
+
export type * from './orchestration.js';
|
|
8
|
+
export type * from './vat-orchestration.js';
|
package/src/types.js
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { Fail } from '@agoric/assert';
|
|
3
|
+
|
|
4
|
+
/** @import { ConnectionId } from '../types'; */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {ConnectionId} hostConnectionId Counterpart Connection ID
|
|
8
|
+
* @param {ConnectionId} controllerConnectionId Self Connection ID
|
|
9
|
+
* @param {object} [opts]
|
|
10
|
+
* @param {string} [opts.encoding] - message encoding format for the channel. default is `proto3`
|
|
11
|
+
* @param {'ordered' | 'unordered'} [opts.ordering] - channel ordering. currently only `ordered` is supported for ics27-1
|
|
12
|
+
* @param {string} [opts.txType] - default is `sdk_multi_msg`
|
|
13
|
+
* @param {string} [opts.version] - default is `ics27-1`
|
|
14
|
+
*/
|
|
15
|
+
export const makeICAConnectionAddress = (
|
|
16
|
+
hostConnectionId,
|
|
17
|
+
controllerConnectionId,
|
|
18
|
+
{
|
|
19
|
+
version = 'ics27-1',
|
|
20
|
+
encoding = 'proto3',
|
|
21
|
+
ordering = 'ordered',
|
|
22
|
+
txType = 'sdk_multi_msg',
|
|
23
|
+
} = {},
|
|
24
|
+
) => {
|
|
25
|
+
hostConnectionId || Fail`hostConnectionId is required`;
|
|
26
|
+
controllerConnectionId || Fail`controllerConnectionId is required`;
|
|
27
|
+
const connString = JSON.stringify({
|
|
28
|
+
version,
|
|
29
|
+
controllerConnectionId,
|
|
30
|
+
hostConnectionId,
|
|
31
|
+
address: '', // will be provided by the counterparty after channelOpenAck
|
|
32
|
+
encoding,
|
|
33
|
+
txType,
|
|
34
|
+
});
|
|
35
|
+
return `/ibc-hop/${controllerConnectionId}/ibc-port/icahost/${ordering}/${connString}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse a chain address from a remote address string.
|
|
40
|
+
* Assumes the address string is in a JSON format and contains an "address" field.
|
|
41
|
+
* This function is designed to be safe against malformed inputs and unexpected data types, and will return `undefined` in those cases.
|
|
42
|
+
* @param {string} remoteAddressString - remote address string, including version
|
|
43
|
+
* @returns {string | undefined} returns undefined on error
|
|
44
|
+
*/
|
|
45
|
+
export const parseAddress = remoteAddressString => {
|
|
46
|
+
try {
|
|
47
|
+
// Extract JSON version string assuming it's always surrounded by {}
|
|
48
|
+
const jsonStr = remoteAddressString?.match(/{.*?}/)?.[0];
|
|
49
|
+
const jsonObj = jsonStr ? JSON.parse(jsonStr) : undefined;
|
|
50
|
+
return jsonObj?.address ?? undefined;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { Far } from '@endo/far';
|
|
3
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
4
|
+
import { prepareOrchestrationTools } from './orchestration.js';
|
|
5
|
+
|
|
6
|
+
/** @import { OrchestrationPowers } from './types.js' */
|
|
7
|
+
|
|
8
|
+
export const buildRootObject = (_vatPowers, _args, baggage) => {
|
|
9
|
+
const zone = makeDurableZone(baggage);
|
|
10
|
+
const { makeOrchestration } = prepareOrchestrationTools(
|
|
11
|
+
zone.subZone('orchestration'),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
return Far('OrchestrationVat', {
|
|
15
|
+
/** @param {Partial<OrchestrationPowers>} [initialPowers] */
|
|
16
|
+
makeOrchestration(initialPowers = {}) {
|
|
17
|
+
return makeOrchestration(initialPowers);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** @typedef {ReturnType<typeof buildRootObject>} OrchestrationVat */
|