@agoric/deploy-script-support 0.10.4-dev-74b2609.0.74b2609 → 0.10.4-dev-9480fc7.0.9480fc7
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 +19 -11
- package/src/control/attenuated-deposit.core.js +55 -0
- package/src/control/chain-info.core.js +187 -0
- package/src/control/contract-control.build.js +27 -0
- package/src/control/contract-control.contract.js +386 -0
- package/src/control/contract-control.core.js +138 -0
- package/src/control/get-upgrade-kit.build.js +28 -0
- package/src/control/get-upgrade-kit.core.js +83 -0
- package/src/control/postal-service.core.js +59 -0
- package/src/wallet-utils.js +150 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/deploy-script-support",
|
|
3
|
-
"version": "0.10.4-dev-
|
|
3
|
+
"version": "0.10.4-dev-9480fc7.0.9480fc7",
|
|
4
4
|
"description": "Helpers and other support for writing deploy scripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/helpers.js",
|
|
@@ -34,25 +34,33 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@agoric/
|
|
38
|
-
"@agoric/
|
|
39
|
-
"@agoric/
|
|
40
|
-
"@agoric/
|
|
41
|
-
"@agoric/
|
|
42
|
-
"@agoric/
|
|
43
|
-
"@agoric/
|
|
44
|
-
"@agoric/
|
|
37
|
+
"@agoric/client-utils": "0.1.1-dev-9480fc7.0.9480fc7",
|
|
38
|
+
"@agoric/ertp": "0.16.3-dev-9480fc7.0.9480fc7",
|
|
39
|
+
"@agoric/import-manager": "0.3.12-dev-9480fc7.0.9480fc7",
|
|
40
|
+
"@agoric/internal": "0.3.3-dev-9480fc7.0.9480fc7",
|
|
41
|
+
"@agoric/notifier": "0.6.3-dev-9480fc7.0.9480fc7",
|
|
42
|
+
"@agoric/orchestration": "0.1.1-dev-9480fc7.0.9480fc7",
|
|
43
|
+
"@agoric/pola-io": "0.1.1-dev-9480fc7.0.9480fc7",
|
|
44
|
+
"@agoric/portfolio-api": "0.1.1-dev-9480fc7.0.9480fc7",
|
|
45
|
+
"@agoric/smart-wallet": "0.5.4-dev-9480fc7.0.9480fc7",
|
|
46
|
+
"@agoric/store": "0.9.3-dev-9480fc7.0.9480fc7",
|
|
47
|
+
"@agoric/time": "0.3.3-dev-9480fc7.0.9480fc7",
|
|
48
|
+
"@agoric/zoe": "0.26.3-dev-9480fc7.0.9480fc7",
|
|
49
|
+
"@agoric/zone": "0.2.3-dev-9480fc7.0.9480fc7",
|
|
45
50
|
"@endo/base64": "^1.0.12",
|
|
46
51
|
"@endo/bundle-source": "^4.1.2",
|
|
47
52
|
"@endo/errors": "^1.2.13",
|
|
53
|
+
"@endo/eventual-send": "^1.3.4",
|
|
48
54
|
"@endo/far": "^1.1.14",
|
|
49
55
|
"@endo/marshal": "^1.8.0",
|
|
50
56
|
"@endo/nat": "^5.1.3",
|
|
57
|
+
"@endo/pass-style": "^1.6.3",
|
|
58
|
+
"@endo/patterns": "^1.7.0",
|
|
51
59
|
"@endo/promise-kit": "^1.1.13",
|
|
52
60
|
"@endo/zip": "^1.0.11"
|
|
53
61
|
},
|
|
54
62
|
"devDependencies": {
|
|
55
|
-
"@agoric/vats": "0.15.2-dev-
|
|
63
|
+
"@agoric/vats": "0.15.2-dev-9480fc7.0.9480fc7",
|
|
56
64
|
"@endo/init": "^1.1.12",
|
|
57
65
|
"ava": "^6.4.1",
|
|
58
66
|
"import-meta-resolve": "^4.1.0"
|
|
@@ -75,5 +83,5 @@
|
|
|
75
83
|
"typeCoverage": {
|
|
76
84
|
"atLeast": 84.46
|
|
77
85
|
},
|
|
78
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "9480fc7ebf0d0bf38520ed118e240bdbb3ec82c9"
|
|
79
87
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Get a promise for a deposit facet without
|
|
3
|
+
* access to all of namesByAddressAdmin.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { E } from '@endo/eventual-send';
|
|
7
|
+
|
|
8
|
+
const trace = (...args) => console.log('---- AttD', ...args);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @import {DepositFacet} from '@agoric/ertp';
|
|
12
|
+
* @import {BootstrapPowers, PromiseSpaceOf} from '@agoric/vats/src/core/types.js';
|
|
13
|
+
*/
|
|
14
|
+
const depositFacetKey = 'depositFacet';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* XXX move this into BootstrapPowers
|
|
18
|
+
* @typedef {PromiseSpaceOf<{
|
|
19
|
+
* getDepositFacet: (addr: string, debugName?: string) => Promise<DepositFacet>
|
|
20
|
+
* }>} AttenuatedDepositPowers
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {BootstrapPowers & AttenuatedDepositPowers} powers
|
|
25
|
+
*/
|
|
26
|
+
export const produceAttenuatedDeposit = powers => {
|
|
27
|
+
powers.produce.getDepositFacet.reset();
|
|
28
|
+
const { namesByAddress, namesByAddressAdmin } = powers.consume;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param {string} addr
|
|
33
|
+
* @param {string} [debugName]
|
|
34
|
+
*/
|
|
35
|
+
const getDepositFacet = async (addr, debugName = 'party') => {
|
|
36
|
+
trace('reserve nameHub for', debugName, addr);
|
|
37
|
+
await E(namesByAddressAdmin).reserve(addr);
|
|
38
|
+
trace('lookup depositFacet for', debugName, addr);
|
|
39
|
+
const df = await E(namesByAddress).lookup(addr, depositFacetKey);
|
|
40
|
+
trace('got depositFacet for', debugName, addr);
|
|
41
|
+
return df;
|
|
42
|
+
};
|
|
43
|
+
harden(getDepositFacet);
|
|
44
|
+
|
|
45
|
+
powers.produce.getDepositFacet.resolve(getDepositFacet);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const getManifestForAttenuatedDeposit = () => ({
|
|
49
|
+
manifest: {
|
|
50
|
+
[produceAttenuatedDeposit.name]: {
|
|
51
|
+
consume: { namesByAddress: true, namesByAddressAdmin: true },
|
|
52
|
+
produce: { getDepositFacet: true },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { makeTracer } from '@agoric/internal';
|
|
2
|
+
import { registerChain } from '@agoric/orchestration/src/chain-info.js';
|
|
3
|
+
import { Fail } from '@endo/errors';
|
|
4
|
+
import { E, Far } from '@endo/far';
|
|
5
|
+
import { makeMarshal } from '@endo/marshal';
|
|
6
|
+
|
|
7
|
+
// TODO: refactor overlap with init-chain-info.js in orch pkg
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @import {Remote, ERemote} from '@agoric/internal';
|
|
11
|
+
* @import {ChainInfo} from '@agoric/orchestration';
|
|
12
|
+
* @import {NameHub, NameAdmin} from '@agoric/vats';
|
|
13
|
+
* @typedef {Record<string, { chainInfo: ChainInfo }>} AxelarChainConfigMap
|
|
14
|
+
* @import {ERef} from '@endo/eventual-send';
|
|
15
|
+
* @import {StorageNode} from '@agoric/internal/src/lib-chainStorage.js';
|
|
16
|
+
* @import {PromiseSpaceOf} from '@agoric/vats/src/core/types.js';
|
|
17
|
+
* @import {BootstrapPowers} from '@agoric/vats/src/core/types.js';
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const trace = makeTracer('ChainInfoCore', true);
|
|
21
|
+
|
|
22
|
+
// chainInfo has no cap data but we need to marshal bigints
|
|
23
|
+
const marshalData = makeMarshal(_val => Fail`data only`);
|
|
24
|
+
|
|
25
|
+
// See also: exos/chain-hub. Consistency is enforced by test.
|
|
26
|
+
export const HubName = {
|
|
27
|
+
Chain: 'chain',
|
|
28
|
+
ChainConnection: 'chainConnection',
|
|
29
|
+
ChainAssets: 'chainAssets',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Similar to publishAgoricNamesToChainStorage but publishes a node per chain
|
|
34
|
+
* instead of one list of entries
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* For each HubName, provide a NameHubKit reflected into vstorage unless there's
|
|
39
|
+
* already an agorcNames key by that name.
|
|
40
|
+
*
|
|
41
|
+
* @param {ERef<NameAdmin>} agoricNamesAdmin
|
|
42
|
+
* @param {ERemote<StorageNode>} agoricNamesNode
|
|
43
|
+
* @param {ERef<NameHub>} agoricNames
|
|
44
|
+
*/
|
|
45
|
+
const publishChainInfoToChainStorage = async (
|
|
46
|
+
agoricNamesAdmin,
|
|
47
|
+
agoricNamesNode,
|
|
48
|
+
agoricNames,
|
|
49
|
+
) => {
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} subpath
|
|
52
|
+
*/
|
|
53
|
+
const echoNameUpdates = async subpath => {
|
|
54
|
+
trace('reflecting', subpath, 'from agoricNames to vstorage');
|
|
55
|
+
const chainNamesNode = E(agoricNamesNode).makeChildNode(subpath);
|
|
56
|
+
const { nameAdmin } = await E(agoricNamesAdmin).provideChild(subpath);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Previous entries, to prevent redundant updates
|
|
60
|
+
*
|
|
61
|
+
* @type {Record<string, string>} chainName => stringified chainInfo
|
|
62
|
+
*/
|
|
63
|
+
const prev = {};
|
|
64
|
+
|
|
65
|
+
// XXX cannot be changed until we upgrade vat-agoricNames to allow it
|
|
66
|
+
await E(nameAdmin).onUpdate(
|
|
67
|
+
// XXX will live on the heap in the bootstrap vat. When we upgrade or kill
|
|
68
|
+
// that this handler will sever and vat-agoricNames will need to be upgraded
|
|
69
|
+
// to allow changing the handler, or to use pubsub mechanics instead.
|
|
70
|
+
Far('chain info writer', {
|
|
71
|
+
write(entries) {
|
|
72
|
+
for (const [chainName, info] of entries) {
|
|
73
|
+
const value = JSON.stringify(marshalData.toCapData(info));
|
|
74
|
+
if (prev[chainName] === value) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const chainNode = E(chainNamesNode).makeChildNode(chainName);
|
|
78
|
+
prev[chainName] = value;
|
|
79
|
+
void E(chainNode)
|
|
80
|
+
.setValue(value)
|
|
81
|
+
.catch(() => delete prev[chainName]);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
const existingKeys = await E(agoricNames).keys();
|
|
88
|
+
await Promise.all(
|
|
89
|
+
Object.values(HubName)
|
|
90
|
+
.filter(k => !existingKeys.includes(k))
|
|
91
|
+
.map(echoNameUpdates),
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* null chainStorage case is vestigial
|
|
97
|
+
*
|
|
98
|
+
* @typedef {{ consume: { chainStorage: Promise<Remote<StorageNode>> } }} ChainStoragePresent
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* XXX move this into BootstrapPowers
|
|
103
|
+
* @typedef {PromiseSpaceOf<{
|
|
104
|
+
* chainInfoPublished: unknown
|
|
105
|
+
* }>} ChainInfoPowers
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* WARNING: prunes any data that was previously published
|
|
110
|
+
*
|
|
111
|
+
* @param {BootstrapPowers & ChainStoragePresent & ChainInfoPowers} powers
|
|
112
|
+
* @param {{
|
|
113
|
+
* options: {
|
|
114
|
+
* chainInfo?: Record<string, ChainInfo>;
|
|
115
|
+
* axelarConfig: AxelarChainConfigMap;
|
|
116
|
+
* };
|
|
117
|
+
* }} config
|
|
118
|
+
*/
|
|
119
|
+
export const publishChainInfo = async (
|
|
120
|
+
{
|
|
121
|
+
consume: { agoricNames, agoricNamesAdmin, chainStorage },
|
|
122
|
+
produce: { chainInfoPublished },
|
|
123
|
+
},
|
|
124
|
+
config,
|
|
125
|
+
) => {
|
|
126
|
+
const { keys } = Object;
|
|
127
|
+
const { chainInfo = {} } = config.options;
|
|
128
|
+
trace('publishChainInfo', keys(chainInfo));
|
|
129
|
+
|
|
130
|
+
const agoricNamesNode = E(chainStorage).makeChildNode('agoricNames');
|
|
131
|
+
|
|
132
|
+
// Ensure updates go to vstorage
|
|
133
|
+
await publishChainInfoToChainStorage(
|
|
134
|
+
agoricNamesAdmin,
|
|
135
|
+
agoricNamesNode,
|
|
136
|
+
agoricNames,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
for (const kind of Object.values(HubName)) {
|
|
140
|
+
const hub = E(agoricNames).lookup(kind);
|
|
141
|
+
/** @type {string[]} */
|
|
142
|
+
const oldKeys = await E(hub).keys();
|
|
143
|
+
trace('clearing old', kind, oldKeys);
|
|
144
|
+
if (!oldKeys.length) continue;
|
|
145
|
+
|
|
146
|
+
const admin = E(agoricNamesAdmin).lookupAdmin(kind);
|
|
147
|
+
await Promise.all(oldKeys.map(k => E(admin).delete(k)));
|
|
148
|
+
const node = E(agoricNamesNode).makeChildNode(kind);
|
|
149
|
+
// XXX setValue('') deletes a vstorage key (right?)
|
|
150
|
+
await Promise.all(
|
|
151
|
+
oldKeys.map(k =>
|
|
152
|
+
E(E(node).makeChildNode(k, { sequence: false })).setValue(''),
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const handledConnections = new Set();
|
|
158
|
+
for await (const [name, info] of Object.entries(chainInfo)) {
|
|
159
|
+
await registerChain(
|
|
160
|
+
agoricNamesAdmin,
|
|
161
|
+
name,
|
|
162
|
+
info,
|
|
163
|
+
trace,
|
|
164
|
+
handledConnections,
|
|
165
|
+
);
|
|
166
|
+
trace('@@@registered', name, info);
|
|
167
|
+
}
|
|
168
|
+
trace('@@@conn', ...handledConnections);
|
|
169
|
+
|
|
170
|
+
chainInfoPublished.resolve(true);
|
|
171
|
+
trace('publishChainInfo done');
|
|
172
|
+
};
|
|
173
|
+
harden(publishChainInfo);
|
|
174
|
+
|
|
175
|
+
export const getManifestForChainInfo = (_u, { options }) => ({
|
|
176
|
+
manifest: {
|
|
177
|
+
[publishChainInfo.name]: {
|
|
178
|
+
consume: {
|
|
179
|
+
agoricNames: true,
|
|
180
|
+
agoricNamesAdmin: true,
|
|
181
|
+
chainStorage: true,
|
|
182
|
+
},
|
|
183
|
+
produce: { chainInfoPublished: true },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
options,
|
|
187
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { CoreEvalBuilder, DeployScriptFunction } from '@agoric/deploy-script-support/src/externalTypes.js';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { makeHelpers } from '../helpers.js';
|
|
6
|
+
import { getManifestForDeliverContractControl } from './contract-control.core.js';
|
|
7
|
+
|
|
8
|
+
const sourceSpec = './contract-control.core.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @satisfies {CoreEvalBuilder}
|
|
12
|
+
*/
|
|
13
|
+
const defaultProposalBuilder = async () => {
|
|
14
|
+
return harden({
|
|
15
|
+
sourceSpec,
|
|
16
|
+
getManifestCall: [getManifestForDeliverContractControl.name],
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** @type {DeployScriptFunction} */
|
|
21
|
+
const build = async (homeP, endowments) => {
|
|
22
|
+
const { writeCoreEval } = await makeHelpers(homeP, endowments);
|
|
23
|
+
|
|
24
|
+
await writeCoreEval('eval-contract-control', () => defaultProposalBuilder());
|
|
25
|
+
};
|
|
26
|
+
harden(build);
|
|
27
|
+
export default build;
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file contract-control: install, upgrade, terminate, reset chain-storage,
|
|
3
|
+
* for a contract in a branch of agoricNames / chain-storage.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { makeTracer } from '@agoric/internal/src/debug.js';
|
|
7
|
+
import { Fail, q } from '@endo/errors';
|
|
8
|
+
import { E, passStyleOf } from '@endo/far';
|
|
9
|
+
import { M, objectMap } from '@endo/patterns';
|
|
10
|
+
|
|
11
|
+
const trace = makeTracer('CCtrl');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @import {Zone} from '@agoric/base-zone';
|
|
15
|
+
* @import {ContractStartFunction, StartResult} from '@agoric/zoe/src/zoeService/utils.js';
|
|
16
|
+
* @import {Installation, Instance, IssuerKeywordRecord, ZoeService} from '@agoric/zoe';
|
|
17
|
+
* @import {Remote} from '@agoric/internal';
|
|
18
|
+
* @import {StorageNode} from '@agoric/internal/src/lib-chainStorage.js';
|
|
19
|
+
* @import {Board, NameHubKit} from '@agoric/vats';
|
|
20
|
+
* @import {ERef} from '@agoric/vow';
|
|
21
|
+
* @import {StartedInstanceKitWithLabel, StartUpgradable} from '@agoric/vats/src/core/types.js';
|
|
22
|
+
* @import {StartedInstanceKit} from '@agoric/vats/src/core/types.js';
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {<SF extends ContractStartFunction>(instance: Instance<SF>, privateArgs: Parameters<SF>[1]) => void} UpdatePrivateArgs
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const IssuerShape = M.remotable('Issuer');
|
|
30
|
+
const InstallationShape = M.remotable('Installation');
|
|
31
|
+
const PublicParts = harden({
|
|
32
|
+
label: M.string(),
|
|
33
|
+
instance: M.remotable('Instance'),
|
|
34
|
+
publicFacet: M.remotable('publicFacet'),
|
|
35
|
+
});
|
|
36
|
+
const BundleIdShape = M.string();
|
|
37
|
+
const VatUpgradeResultsShape = M.splitRecord({ incarnationNumber: M.number() });
|
|
38
|
+
const IssuersShape = M.recordOf(M.string(), IssuerShape);
|
|
39
|
+
const PrivateArgsOverridesShape = M.record();
|
|
40
|
+
|
|
41
|
+
const iface = M.interface('ContractControl', {
|
|
42
|
+
install: M.callWhen(M.string()).returns({
|
|
43
|
+
boardId: M.string(),
|
|
44
|
+
installation: InstallationShape,
|
|
45
|
+
}),
|
|
46
|
+
start: M.callWhen(
|
|
47
|
+
M.splitRecord(
|
|
48
|
+
{ installation: InstallationShape },
|
|
49
|
+
{
|
|
50
|
+
issuers: IssuersShape,
|
|
51
|
+
privateArgsOverrides: PrivateArgsOverridesShape,
|
|
52
|
+
},
|
|
53
|
+
{}, // Refuse unsupported options
|
|
54
|
+
),
|
|
55
|
+
).returns(PublicParts),
|
|
56
|
+
installAndStart: M.callWhen(
|
|
57
|
+
M.splitRecord(
|
|
58
|
+
{ bundleId: BundleIdShape },
|
|
59
|
+
{
|
|
60
|
+
issuers: IssuersShape,
|
|
61
|
+
privateArgsOverrides: PrivateArgsOverridesShape,
|
|
62
|
+
},
|
|
63
|
+
{}, // Refuse unsupported options
|
|
64
|
+
),
|
|
65
|
+
).returns(PublicParts),
|
|
66
|
+
getPublicFacet: M.call().returns(M.remotable('publicFacet')),
|
|
67
|
+
getCreatorFacet: M.call().returns(M.remotable('creatorFacet')),
|
|
68
|
+
upgrade: M.callWhen(
|
|
69
|
+
M.or(
|
|
70
|
+
BundleIdShape, // Backward compat with old signature
|
|
71
|
+
M.splitRecord(
|
|
72
|
+
{ bundleId: BundleIdShape },
|
|
73
|
+
{
|
|
74
|
+
privateArgsOverrides: PrivateArgsOverridesShape,
|
|
75
|
+
},
|
|
76
|
+
{}, // Refuse unsupported options
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
).returns(VatUpgradeResultsShape),
|
|
80
|
+
terminate: M.callWhen()
|
|
81
|
+
.optional(
|
|
82
|
+
M.splitRecord(
|
|
83
|
+
{},
|
|
84
|
+
{ message: M.string(), target: M.string(), revoke: M.boolean() },
|
|
85
|
+
{}, // Refuse unsupported options
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
.returns(),
|
|
89
|
+
pruneChainStorage: M.callWhen(
|
|
90
|
+
M.recordOf(M.string(), M.arrayOf(M.string())),
|
|
91
|
+
).returns(M.number()),
|
|
92
|
+
revoke: M.call().returns(),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {Zone} zone
|
|
97
|
+
* @param {{
|
|
98
|
+
* agoricNamesAdmin: ERef<NameHubKit['nameAdmin']>,
|
|
99
|
+
* board: ERef<Board>,
|
|
100
|
+
* zoe: ERef<ZoeService>,
|
|
101
|
+
* startUpgradable: ERef<StartUpgradable>,
|
|
102
|
+
* updatePrivateArgs: ERef<UpdatePrivateArgs>
|
|
103
|
+
* }} svcs
|
|
104
|
+
*/
|
|
105
|
+
export const prepareContractControl = (zone, svcs) => {
|
|
106
|
+
/** @template {ContractStartFunction} SF */
|
|
107
|
+
const makeMaker = () =>
|
|
108
|
+
zone.exoClass(
|
|
109
|
+
'ContractControl',
|
|
110
|
+
iface,
|
|
111
|
+
/**
|
|
112
|
+
* Install by bundleId and publish to (the board and) agoricNames,
|
|
113
|
+
* replacing anything that was there before.
|
|
114
|
+
*
|
|
115
|
+
* Former installations can be reached from the board.
|
|
116
|
+
*
|
|
117
|
+
* @param {ContractControlOpts} initial
|
|
118
|
+
*/
|
|
119
|
+
initial => ({
|
|
120
|
+
initialPrivateArgs:
|
|
121
|
+
initial.initialPrivateArgs || initial.kit?.privateArgs,
|
|
122
|
+
kit: undefined,
|
|
123
|
+
...initial,
|
|
124
|
+
revoked: false,
|
|
125
|
+
}),
|
|
126
|
+
{
|
|
127
|
+
/** @param {string} bundleId */
|
|
128
|
+
async install(bundleId) {
|
|
129
|
+
const { name, revoked } = this.state;
|
|
130
|
+
trace(name, 'install', bundleId);
|
|
131
|
+
!revoked || Fail`revoked`;
|
|
132
|
+
const { zoe, board, agoricNamesAdmin } = svcs;
|
|
133
|
+
const installation = await E(zoe).installBundleID(bundleId);
|
|
134
|
+
const installationAdmin =
|
|
135
|
+
E(agoricNamesAdmin).lookupAdmin('installation');
|
|
136
|
+
await E(installationAdmin).update(name, installation);
|
|
137
|
+
const boardId = await E(board).getId(installation);
|
|
138
|
+
trace(name, 'installed', { bundleId, installation, boardId });
|
|
139
|
+
return { boardId, installation };
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Start the contract; publish the instance to (the board and) agoricNames.
|
|
144
|
+
*
|
|
145
|
+
* @throws if already running
|
|
146
|
+
* @param {object} opts
|
|
147
|
+
* @param {Installation<SF>} opts.installation
|
|
148
|
+
* @param {IssuerKeywordRecord} [opts.issuers]
|
|
149
|
+
* @param {Partial<Parameters<SF>[1]>} [opts.privateArgsOverrides]
|
|
150
|
+
*/
|
|
151
|
+
async start({ installation, issuers, privateArgsOverrides }) {
|
|
152
|
+
const { name, storageNode, revoked, initialPrivateArgs } = this.state;
|
|
153
|
+
!revoked || Fail`revoked`;
|
|
154
|
+
!this.state.kit || Fail`${name} already started`;
|
|
155
|
+
const { startUpgradable, board } = svcs;
|
|
156
|
+
const installationId = await E(board).getId(installation);
|
|
157
|
+
trace(name, 'startUpgradable', { installation, installationId });
|
|
158
|
+
const privateArgs = harden({
|
|
159
|
+
...initialPrivateArgs,
|
|
160
|
+
storageNode,
|
|
161
|
+
...privateArgsOverrides,
|
|
162
|
+
});
|
|
163
|
+
const kit = await E(startUpgradable)({
|
|
164
|
+
label: name,
|
|
165
|
+
installation,
|
|
166
|
+
issuerKeywordRecord: issuers,
|
|
167
|
+
// terms: customTerms,
|
|
168
|
+
privateArgs,
|
|
169
|
+
});
|
|
170
|
+
/** @type {StartedInstanceKitWithLabel<SF>} */
|
|
171
|
+
const fullKit = harden({ ...kit, privateArgs });
|
|
172
|
+
trace(name, 'started', objectMap(fullKit, passStyleOf));
|
|
173
|
+
this.state.kit = fullKit;
|
|
174
|
+
|
|
175
|
+
const { agoricNamesAdmin } = svcs;
|
|
176
|
+
const instanceAdmin = E(agoricNamesAdmin).lookupAdmin('instance');
|
|
177
|
+
await E(instanceAdmin).update(name, kit.instance);
|
|
178
|
+
const boardId = await E(board).getId(kit.instance);
|
|
179
|
+
trace(name, 'published', { boardId, installationId });
|
|
180
|
+
|
|
181
|
+
return harden({
|
|
182
|
+
label: name,
|
|
183
|
+
instance: kit.instance,
|
|
184
|
+
publicFacet: kit.publicFacet,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {object} opts
|
|
190
|
+
* @param {string} opts.bundleId
|
|
191
|
+
* @param {IssuerKeywordRecord} [opts.issuers]
|
|
192
|
+
* @param {Partial<Parameters<SF>[1]>} [opts.privateArgsOverrides]
|
|
193
|
+
*/
|
|
194
|
+
async installAndStart({ bundleId, issuers, privateArgsOverrides }) {
|
|
195
|
+
trace(this.state.name, 'installAndStart');
|
|
196
|
+
!this.state.revoked || Fail`revoked`;
|
|
197
|
+
const { self } = this;
|
|
198
|
+
const { installation } = await self.install(bundleId);
|
|
199
|
+
return self.start({
|
|
200
|
+
installation,
|
|
201
|
+
issuers,
|
|
202
|
+
privateArgsOverrides,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/** @returns {StartResult<SF>['publicFacet']} */
|
|
207
|
+
getPublicFacet() {
|
|
208
|
+
const { name, revoked, kit } = this.state;
|
|
209
|
+
trace(name, 'getPublicFacet');
|
|
210
|
+
!revoked || Fail`revoked`;
|
|
211
|
+
if (!kit) throw Fail`${q(name)}: no StartedInstanceKit`;
|
|
212
|
+
return kit.publicFacet;
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
/** @returns {StartResult<SF>['creatorFacet']} */
|
|
216
|
+
getCreatorFacet() {
|
|
217
|
+
const { name, revoked, kit } = this.state;
|
|
218
|
+
trace(name, 'getCreatorFacet');
|
|
219
|
+
!revoked || Fail`revoked`;
|
|
220
|
+
if (!kit) throw Fail`${q(name)}: no StartedInstanceKit`;
|
|
221
|
+
return kit.creatorFacet;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
/** @param {string | {bundleId: string; privateArgsOverrides?: Partial<Parameters<SF>[1]>; }} opts */
|
|
225
|
+
async upgrade(opts) {
|
|
226
|
+
if (typeof opts === 'string') opts = { bundleId: opts };
|
|
227
|
+
const { bundleId, privateArgsOverrides = {} } = opts;
|
|
228
|
+
|
|
229
|
+
const { updatePrivateArgs } = svcs;
|
|
230
|
+
|
|
231
|
+
const { name, revoked, kit, initialPrivateArgs } = this.state;
|
|
232
|
+
trace(name, 'upgrade', bundleId);
|
|
233
|
+
!revoked || Fail`revoked`;
|
|
234
|
+
if (!kit) throw Fail`${q(name)}: no StartedInstanceKit`;
|
|
235
|
+
const { privateArgs: previousPrivateArgs } = kit;
|
|
236
|
+
const privateArgs = {
|
|
237
|
+
...initialPrivateArgs,
|
|
238
|
+
...previousPrivateArgs,
|
|
239
|
+
...privateArgsOverrides,
|
|
240
|
+
};
|
|
241
|
+
const result = await E(kit.adminFacet).upgradeContract(
|
|
242
|
+
bundleId,
|
|
243
|
+
privateArgs,
|
|
244
|
+
);
|
|
245
|
+
const newKit = harden({ ...kit, privateArgs });
|
|
246
|
+
trace(name, 'upgrade result', result);
|
|
247
|
+
this.state.kit = newKit;
|
|
248
|
+
await E(updatePrivateArgs)(kit.instance, privateArgs);
|
|
249
|
+
return result;
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {object} opts
|
|
254
|
+
* @param {string} [opts.target] boardId to confirm is current
|
|
255
|
+
* @param {string} [opts.message] for termination error
|
|
256
|
+
* @param {boolean} [opts.revoke] neuter this object
|
|
257
|
+
*/
|
|
258
|
+
async terminate(opts = {}) {
|
|
259
|
+
const { name, kit, revoked } = this.state;
|
|
260
|
+
trace(name, 'terminate', kit?.adminFacet, opts);
|
|
261
|
+
!revoked || Fail`revoked`;
|
|
262
|
+
const { target, message = 'terminated', revoke } = opts;
|
|
263
|
+
if (!kit) {
|
|
264
|
+
if (revoke) {
|
|
265
|
+
trace(name, 'revoked');
|
|
266
|
+
this.state.revoked = true;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
throw Fail`${q(name)}: no StartedInstanceKit`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
await null;
|
|
273
|
+
if (target) {
|
|
274
|
+
const current = await E(svcs.board).getId(kit.instance);
|
|
275
|
+
assert.equal(current, target);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await E(kit.adminFacet).terminateContract(harden(Error(message)));
|
|
280
|
+
} catch (err) {
|
|
281
|
+
console.error('terminateContract failed; forgetting kit', err);
|
|
282
|
+
}
|
|
283
|
+
this.state.kit = undefined;
|
|
284
|
+
if (revoke) {
|
|
285
|
+
trace(name, 'revoked');
|
|
286
|
+
this.state.revoked = true;
|
|
287
|
+
}
|
|
288
|
+
const { agoricNamesAdmin } = svcs;
|
|
289
|
+
const instanceAdmin = E(agoricNamesAdmin).lookupAdmin('instance');
|
|
290
|
+
await E(instanceAdmin).delete(name);
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
/** @param {Record<string, string[]>} parentToChildren */
|
|
294
|
+
async pruneChainStorage(parentToChildren) {
|
|
295
|
+
const { name, storageNode, revoked } = this.state;
|
|
296
|
+
trace(
|
|
297
|
+
name,
|
|
298
|
+
'pruneChainStorage',
|
|
299
|
+
Object.keys(parentToChildren).length,
|
|
300
|
+
);
|
|
301
|
+
!revoked || Fail`revoked`;
|
|
302
|
+
|
|
303
|
+
/** @param {string[]} path */
|
|
304
|
+
const makePathNode = path => {
|
|
305
|
+
/** @type {Promise<Remote<StorageNode>>} */
|
|
306
|
+
let node = Promise.resolve(storageNode);
|
|
307
|
+
for (const segment of path) {
|
|
308
|
+
node = E(node).makeChildNode(segment);
|
|
309
|
+
}
|
|
310
|
+
return node;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const prefix = await E(storageNode).getPath();
|
|
314
|
+
for (const parent of Object.keys(parentToChildren)) {
|
|
315
|
+
parent.startsWith(prefix) ||
|
|
316
|
+
Fail`${parent} must start with ${prefix}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let qty = 0;
|
|
320
|
+
for (const [parent, children] of Object.entries(parentToChildren)) {
|
|
321
|
+
trace(name, 'pruning', parent, children);
|
|
322
|
+
const suffix = parent.slice(prefix.length + '.'.length);
|
|
323
|
+
const segments = suffix ? suffix.split('.') : [];
|
|
324
|
+
const parentNode = makePathNode(segments);
|
|
325
|
+
// on failure, trace rather than aborting the whole job
|
|
326
|
+
await Promise.allSettled(
|
|
327
|
+
children.map(async k => {
|
|
328
|
+
await null;
|
|
329
|
+
try {
|
|
330
|
+
await E(
|
|
331
|
+
E(parentNode).makeChildNode(k, { sequence: false }),
|
|
332
|
+
).setValue('');
|
|
333
|
+
qty += 1;
|
|
334
|
+
} catch (err) {
|
|
335
|
+
trace('rejected:', parent, k, err);
|
|
336
|
+
}
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
trace('done');
|
|
341
|
+
return qty;
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
revoke() {
|
|
345
|
+
const { name, kit, revoked } = this.state;
|
|
346
|
+
trace(name, 'revoke', kit?.adminFacet);
|
|
347
|
+
!revoked || Fail`revoked`;
|
|
348
|
+
|
|
349
|
+
trace(name, 'revoked');
|
|
350
|
+
this.state.revoked = true;
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @template {ContractStartFunction} [SF=ContractStartFunction]
|
|
357
|
+
* @typedef {ReturnType<ReturnType<typeof makeMaker<SF>>>} ContractControl
|
|
358
|
+
*/
|
|
359
|
+
|
|
360
|
+
/** @type {<SF extends ContractStartFunction>(initial: ContractControlOpts<SF>) => ContractControl<SF>} */
|
|
361
|
+
const makeContractControl = makeMaker();
|
|
362
|
+
|
|
363
|
+
return makeContractControl;
|
|
364
|
+
};
|
|
365
|
+
harden(prepareContractControl);
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @template {ContractStartFunction} [SF=ContractStartFunction]
|
|
369
|
+
* @typedef {object} ContractControlOpts
|
|
370
|
+
* @property {string} name contractName
|
|
371
|
+
* @property {Remote<StorageNode>} storageNode
|
|
372
|
+
* @property {StartedInstanceKit<SF> & { privateArgs?: Parameters<SF>[1] }} [kit]
|
|
373
|
+
* @property {Partial<Parameters<SF>[1]>} [initialPrivateArgs]
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
/** @typedef {ReturnType<typeof prepareContractControl>} MakeContractControl */
|
|
377
|
+
|
|
378
|
+
// Hack to allow extracting the generic result of MakeContractControl
|
|
379
|
+
// See https://github.com/microsoft/TypeScript/issues/62524
|
|
380
|
+
// eslint-disable-next-line
|
|
381
|
+
const maker = /** @type {MakeContractControl} */ (
|
|
382
|
+
/** @type {unknown} */ (undefined)
|
|
383
|
+
);
|
|
384
|
+
harden(maker);
|
|
385
|
+
|
|
386
|
+
/** @template {ContractStartFunction} [SF=ContractStartFunction] @typedef {ReturnType<typeof maker<SF>>} ContractControl */
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** @file core eval to deliver control of a contract to a smartWallet */
|
|
2
|
+
|
|
3
|
+
import { makeTracer } from '@agoric/internal/src/debug.js';
|
|
4
|
+
import { makeHeapZone } from '@agoric/zone';
|
|
5
|
+
import { Fail } from '@endo/errors';
|
|
6
|
+
import { E } from '@endo/eventual-send';
|
|
7
|
+
import { YMAX_CONTROL_WALLET_KEY } from '@agoric/portfolio-api/src/portfolio-constants.js';
|
|
8
|
+
import { prepareContractControl } from './contract-control.contract.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @import {ContractStartFunction} from '@agoric/zoe/src/zoeService/utils.js';
|
|
12
|
+
* @import {ChainStoragePresent} from './chain-info.core.js'
|
|
13
|
+
* @import {PostalServiceBoot} from './postal-service.core.js';
|
|
14
|
+
* @import {AttenuatedDepositPowers} from './attenuated-deposit.core.js';
|
|
15
|
+
* @import {ContractControlOpts, ContractControl, UpdatePrivateArgs} from './contract-control.contract.js';
|
|
16
|
+
* @import {PromiseSpaceOf} from '@agoric/vats/src/core/types.js';
|
|
17
|
+
* @import {BootstrapPowers} from '@agoric/vats/src/core/types.js';
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @template {ContractStartFunction} SF
|
|
22
|
+
* @typedef {Omit<ContractControlOpts<SF>, 'storageNode'> & {controlAddress: string}} ContractControlDeliverOpts
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {<SF extends ContractStartFunction>(opts: ContractControlDeliverOpts<SF>) => Promise<{delivered: Promise<void>, contractControl: ContractControl<SF>}>} DeliverContractControl
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {PromiseSpaceOf<{ deliverContractControl: DeliverContractControl}>} ContractControlPowers
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {BootstrapPowers &
|
|
35
|
+
* ChainStoragePresent &
|
|
36
|
+
* PostalServiceBoot &
|
|
37
|
+
* ContractControlPowers &
|
|
38
|
+
* AttenuatedDepositPowers
|
|
39
|
+
* } permitted
|
|
40
|
+
*/
|
|
41
|
+
export const produceDeliverContractControl = async permitted => {
|
|
42
|
+
permitted.produce.deliverContractControl.reset();
|
|
43
|
+
await null;
|
|
44
|
+
|
|
45
|
+
const { consume } = permitted;
|
|
46
|
+
|
|
47
|
+
const { chainStorage, getDepositFacet, zoe } = consume;
|
|
48
|
+
|
|
49
|
+
const instancePrivateArgs = await consume.instancePrivateArgs;
|
|
50
|
+
|
|
51
|
+
const postalSvcPub = E.when(
|
|
52
|
+
permitted.instance.consume.postalService,
|
|
53
|
+
instance => E(zoe).getPublicFacet(instance),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/** @type {UpdatePrivateArgs} */
|
|
57
|
+
const updatePrivateArgs = (instance, privateArgs) => {
|
|
58
|
+
if (!instancePrivateArgs.has(instance)) {
|
|
59
|
+
Fail`instance doesn't have privateArgs`;
|
|
60
|
+
}
|
|
61
|
+
instancePrivateArgs.set(instance, privateArgs);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Use a heap zone to avoid entanglement with old liveslots
|
|
65
|
+
const zone = makeHeapZone();
|
|
66
|
+
const makeContractControl = prepareContractControl(zone, {
|
|
67
|
+
agoricNamesAdmin: await consume.agoricNamesAdmin,
|
|
68
|
+
board: await consume.board,
|
|
69
|
+
startUpgradable: await consume.startUpgradable,
|
|
70
|
+
updatePrivateArgs,
|
|
71
|
+
zoe: await zoe,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/** @type {DeliverContractControl} */
|
|
75
|
+
const deliverContractControl = async ({
|
|
76
|
+
name: contractName,
|
|
77
|
+
controlAddress,
|
|
78
|
+
...opts
|
|
79
|
+
}) => {
|
|
80
|
+
await null;
|
|
81
|
+
|
|
82
|
+
const trace = makeTracer(`CCtrlCore-${contractName}`);
|
|
83
|
+
|
|
84
|
+
trace('creating contract control and delivering to', controlAddress);
|
|
85
|
+
|
|
86
|
+
const contractControl = makeContractControl({
|
|
87
|
+
name: contractName,
|
|
88
|
+
storageNode: await E(chainStorage).makeChildNode(contractName),
|
|
89
|
+
...opts,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
trace('reserving', controlAddress);
|
|
93
|
+
// This can block if the wallet is not provisioned
|
|
94
|
+
await E(getDepositFacet)(controlAddress);
|
|
95
|
+
|
|
96
|
+
trace(`delivering control`, controlAddress, contractControl);
|
|
97
|
+
// don't block on the recipient's offer
|
|
98
|
+
const delivered = E.when(
|
|
99
|
+
E(postalSvcPub).deliverPrize(
|
|
100
|
+
controlAddress,
|
|
101
|
+
contractControl,
|
|
102
|
+
YMAX_CONTROL_WALLET_KEY,
|
|
103
|
+
),
|
|
104
|
+
() => trace('control received'),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return harden({ delivered, contractControl });
|
|
108
|
+
};
|
|
109
|
+
harden(deliverContractControl);
|
|
110
|
+
|
|
111
|
+
permitted.produce.deliverContractControl.resolve(deliverContractControl);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const getManifestForDeliverContractControl = () => ({
|
|
115
|
+
manifest: {
|
|
116
|
+
[produceDeliverContractControl.name]: {
|
|
117
|
+
consume: {
|
|
118
|
+
agoricNamesAdmin: true,
|
|
119
|
+
board: true,
|
|
120
|
+
chainStorage: true,
|
|
121
|
+
getDepositFacet: true,
|
|
122
|
+
instancePrivateArgs: true,
|
|
123
|
+
startUpgradable: true,
|
|
124
|
+
zoe: true,
|
|
125
|
+
},
|
|
126
|
+
produce: {
|
|
127
|
+
deliverContractControl: true,
|
|
128
|
+
},
|
|
129
|
+
instance: {
|
|
130
|
+
consume: { postalService: true },
|
|
131
|
+
// provide: true,
|
|
132
|
+
},
|
|
133
|
+
installation: {
|
|
134
|
+
// provide: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { CoreEvalBuilder, DeployScriptFunction } from '@agoric/deploy-script-support/src/externalTypes.js';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { makeHelpers } from '../helpers.js';
|
|
6
|
+
import { getManifestForGetUpgradeKit } from './get-upgrade-kit.core.js';
|
|
7
|
+
|
|
8
|
+
const sourceSpec =
|
|
9
|
+
'@agoric/deploy-script-support/src/control/get-upgrade-kit.core.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @satisfies {CoreEvalBuilder}
|
|
13
|
+
*/
|
|
14
|
+
const defaultProposalBuilder = async () => {
|
|
15
|
+
return harden({
|
|
16
|
+
sourceSpec,
|
|
17
|
+
getManifestCall: [getManifestForGetUpgradeKit.name],
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** @type {DeployScriptFunction} */
|
|
22
|
+
const build = async (homeP, endowments) => {
|
|
23
|
+
const { writeCoreEval } = await makeHelpers(homeP, endowments);
|
|
24
|
+
|
|
25
|
+
await writeCoreEval('eval-get-upgrade-kit', () => defaultProposalBuilder());
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default build;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Get a contract upgrade kit by name.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { E } from '@endo/eventual-send';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @import {ContractStartFunction, Instance} from '@agoric/zoe/src/zoeService/utils.js';
|
|
9
|
+
* @import {StartedInstanceKit, StartedInstanceKitWithLabel} from '@agoric/vats/src/core/types.js';
|
|
10
|
+
* @import {PromiseSpaceOf} from '@agoric/vats/src/core/types.js';
|
|
11
|
+
* @import {BootstrapPowers} from '@agoric/vats/src/core/types.js';
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const trace = (...args) => console.log('---- GetUpgradeKit', ...args);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Similar to StartedInstanceKit, but with label and optional privateArgs
|
|
18
|
+
* managed by ContractControl.
|
|
19
|
+
*
|
|
20
|
+
* @template {ContractStartFunction} SF
|
|
21
|
+
* @typedef {StartedInstanceKit<SF> & {
|
|
22
|
+
* label: string;
|
|
23
|
+
* privateArgs?: Parameters<SF>[1];
|
|
24
|
+
* }} UpgradeKit
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `getUpgradeKit` is a power allowing to retrieve a contract upgrade kit by
|
|
29
|
+
* the contract's name, without giving full access to the underlying stores.
|
|
30
|
+
*
|
|
31
|
+
* @typedef {PromiseSpaceOf<{
|
|
32
|
+
* getUpgradeKit: <SF extends ContractStartFunction>(name: string) => Promise<StartedInstanceKitWithLabel<SF>>
|
|
33
|
+
* }>} GetUpgradeKitPowers
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {BootstrapPowers & GetUpgradeKitPowers} powers
|
|
38
|
+
*/
|
|
39
|
+
export const produceGetUpgradeKit = async powers => {
|
|
40
|
+
powers.produce.getUpgradeKit.reset();
|
|
41
|
+
const { consume } = powers;
|
|
42
|
+
|
|
43
|
+
const agoricNames = await consume.agoricNames;
|
|
44
|
+
const contractKits = await consume.contractKits;
|
|
45
|
+
const instancePrivateArgs = await consume.instancePrivateArgs;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @template {ContractStartFunction} SF
|
|
49
|
+
* @param {string} name
|
|
50
|
+
* @returns {Promise<StartedInstanceKitWithLabel<SF>>}
|
|
51
|
+
*/
|
|
52
|
+
const getUpgradeKit = async name => {
|
|
53
|
+
trace('getting instance for', name);
|
|
54
|
+
/** @type {Instance<SF>} */
|
|
55
|
+
const instance = await E(agoricNames).lookup('instance', name);
|
|
56
|
+
|
|
57
|
+
trace('getting contract kit for', name);
|
|
58
|
+
const kit = /** @type {UpgradeKit<SF>} */ (contractKits.get(instance));
|
|
59
|
+
|
|
60
|
+
trace('getting privateArgs for', name);
|
|
61
|
+
const privateArgs = instancePrivateArgs.has(instance)
|
|
62
|
+
? /** @type {Parameters<SF>[1]} */ (instancePrivateArgs.get(instance))
|
|
63
|
+
: undefined;
|
|
64
|
+
|
|
65
|
+
return { ...kit, privateArgs };
|
|
66
|
+
};
|
|
67
|
+
harden(getUpgradeKit);
|
|
68
|
+
|
|
69
|
+
powers.produce.getUpgradeKit.resolve(getUpgradeKit);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const getManifestForGetUpgradeKit = () => ({
|
|
73
|
+
manifest: {
|
|
74
|
+
[produceGetUpgradeKit.name]: {
|
|
75
|
+
consume: {
|
|
76
|
+
agoricNames: true,
|
|
77
|
+
contractKits: true,
|
|
78
|
+
instancePrivateArgs: true,
|
|
79
|
+
},
|
|
80
|
+
produce: { getUpgradeKit: true },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { E } from '@endo/eventual-send';
|
|
2
|
+
import { passStyleOf } from '@endo/pass-style';
|
|
3
|
+
import { objectMap } from '@endo/patterns';
|
|
4
|
+
import { makeTracer } from '@agoric/internal/src/debug.js';
|
|
5
|
+
|
|
6
|
+
const contractName = 'postalService';
|
|
7
|
+
|
|
8
|
+
const trace = makeTracer('CCtrlCore');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @import {Installation} from '@agoric/zoe';
|
|
12
|
+
* @import {Instance} from '@agoric/zoe';
|
|
13
|
+
* @import {PromiseSpaceOf} from '@agoric/vats/src/core/types.js';
|
|
14
|
+
* @import {BootstrapPowers} from '@agoric/vats/src/core/types.js';
|
|
15
|
+
* @import {start} from '@aglocal/portfolio-deploy/src/control/postal-service.contract.js';
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {typeof start} PostalServiceStartFn
|
|
19
|
+
*
|
|
20
|
+
* @typedef {{
|
|
21
|
+
* installation: PromiseSpaceOf<{ postalService: Installation<PostalServiceStartFn>}>;
|
|
22
|
+
* instance: PromiseSpaceOf<{ postalService: Instance<PostalServiceStartFn>}>;
|
|
23
|
+
* }} PostalServiceBoot
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** @param {BootstrapPowers & PostalServiceBoot} permitted */
|
|
27
|
+
export const deployPostalService = async permitted => {
|
|
28
|
+
permitted.instance.produce.postalService.reset();
|
|
29
|
+
const { startUpgradable, namesByAddress } = permitted.consume;
|
|
30
|
+
const { postalService: installationP } = permitted.installation.consume;
|
|
31
|
+
trace('await: installation.postalService');
|
|
32
|
+
const installation = await installationP;
|
|
33
|
+
trace('await: namesByAddress');
|
|
34
|
+
const privateArgs = harden({
|
|
35
|
+
namesByAddress: await namesByAddress,
|
|
36
|
+
});
|
|
37
|
+
trace('await: startUpgradable(...)');
|
|
38
|
+
const kit = await E(startUpgradable)({
|
|
39
|
+
label: contractName,
|
|
40
|
+
installation,
|
|
41
|
+
privateArgs,
|
|
42
|
+
});
|
|
43
|
+
trace('kit', objectMap(kit, passStyleOf));
|
|
44
|
+
permitted.instance.produce.postalService.resolve(kit.instance);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const getManifestForPostalService = (
|
|
48
|
+
{ restoreRef },
|
|
49
|
+
{ installKeys },
|
|
50
|
+
) => ({
|
|
51
|
+
manifest: {
|
|
52
|
+
[deployPostalService.name]: {
|
|
53
|
+
consume: { startUpgradable: true, namesByAddress: true },
|
|
54
|
+
installation: { consume: { postalService: true } },
|
|
55
|
+
instance: { produce: { postalService: true } },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
installations: { postalService: restoreRef(installKeys.postalService) },
|
|
59
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* global globalThis */
|
|
2
|
+
/**
|
|
3
|
+
* @file Reusable wallet utilities for portfolio contract testing and operations
|
|
4
|
+
*
|
|
5
|
+
* This module consolidates wallet-related utilities that were previously
|
|
6
|
+
* duplicated across multichain-testing and a3p-integration.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { retryUntilCondition } from '@agoric/client-utils';
|
|
10
|
+
import { YMAX_CONTROL_WALLET_KEY } from '@agoric/portfolio-api/src/portfolio-constants.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @import {UpdateRecord} from '@agoric/smart-wallet/src/smartWallet.js';
|
|
14
|
+
* @import {RetryOptions} from '@agoric/client-utils';
|
|
15
|
+
* @import {SigningSmartWalletKit} from '@agoric/client-utils';
|
|
16
|
+
* @import {Instance} from '@agoric/zoe';
|
|
17
|
+
* @import {BridgeAction} from '@agoric/smart-wallet/src/smartWallet.js';
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Helper to wait for specific wallet updates
|
|
22
|
+
*
|
|
23
|
+
* This provides a higher-level API over retryUntilCondition for common
|
|
24
|
+
* wallet update patterns.
|
|
25
|
+
*
|
|
26
|
+
* @param {() => Promise<UpdateRecord>} getLastUpdate
|
|
27
|
+
* @param {RetryOptions & {
|
|
28
|
+
* log: (...args: unknown[]) => void;
|
|
29
|
+
* setTimeout: typeof globalThis.setTimeout;
|
|
30
|
+
* }} retryOpts
|
|
31
|
+
*/
|
|
32
|
+
export const walletUpdates = (getLastUpdate, retryOpts) => {
|
|
33
|
+
return harden({
|
|
34
|
+
/**
|
|
35
|
+
* Wait for an invocation to complete
|
|
36
|
+
* @param {string | number} id
|
|
37
|
+
*/
|
|
38
|
+
invocation: async id => {
|
|
39
|
+
const done = /** @type {UpdateRecord & { updated: 'invocation' }} */ (
|
|
40
|
+
await retryUntilCondition(
|
|
41
|
+
getLastUpdate,
|
|
42
|
+
update =>
|
|
43
|
+
update.updated === 'invocation' &&
|
|
44
|
+
update.id === id &&
|
|
45
|
+
!!(update.result || update.error),
|
|
46
|
+
`${id}`,
|
|
47
|
+
retryOpts,
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
if (done.error) throw Error(done.error);
|
|
51
|
+
return done.result;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Wait for an offer to complete
|
|
56
|
+
* @param {string | number} id
|
|
57
|
+
*/
|
|
58
|
+
offerResult: async id => {
|
|
59
|
+
const done = await retryUntilCondition(
|
|
60
|
+
getLastUpdate,
|
|
61
|
+
update =>
|
|
62
|
+
// walletAction implies an error, so also stop on that
|
|
63
|
+
update.updated === 'walletAction' ||
|
|
64
|
+
// if it's offerStatus, it can be in progress until result or error
|
|
65
|
+
(update.updated === 'offerStatus' &&
|
|
66
|
+
update.status.id === id &&
|
|
67
|
+
(!!update.status.result || !!update.status.error)),
|
|
68
|
+
`${id}`,
|
|
69
|
+
retryOpts,
|
|
70
|
+
);
|
|
71
|
+
switch (done.updated) {
|
|
72
|
+
case 'walletAction':
|
|
73
|
+
throw Error(`walletAction failure: ${done.status.error}`);
|
|
74
|
+
case 'offerStatus':
|
|
75
|
+
if (done.status.error) {
|
|
76
|
+
throw Error(`offerStatus failure: ${done.status.error}`);
|
|
77
|
+
}
|
|
78
|
+
if (!done.status.result) {
|
|
79
|
+
throw Error(`offerStatus missing result`);
|
|
80
|
+
}
|
|
81
|
+
return done.status.result;
|
|
82
|
+
default:
|
|
83
|
+
throw Error(`unexpected update type ${done.updated}`);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Redeem the ymaxControl invitation from the postal service
|
|
91
|
+
*
|
|
92
|
+
* This is a common pattern in ymax testing where we need to redeem
|
|
93
|
+
* the control facet invitation delivered via postal service.
|
|
94
|
+
*
|
|
95
|
+
* @param {{
|
|
96
|
+
* sig: SigningSmartWalletKit;
|
|
97
|
+
* postalServiceInstance: Instance;
|
|
98
|
+
* fresh: () => string | number;
|
|
99
|
+
* log?: (...args: unknown[]) => void;
|
|
100
|
+
* }} options
|
|
101
|
+
* @returns {Promise<void>}
|
|
102
|
+
*/
|
|
103
|
+
export const redeemYmaxControlInvitation = async ({
|
|
104
|
+
sig,
|
|
105
|
+
postalServiceInstance,
|
|
106
|
+
fresh,
|
|
107
|
+
log = () => {},
|
|
108
|
+
}) => {
|
|
109
|
+
const id = `redeem-${YMAX_CONTROL_WALLET_KEY}-${fresh()}`;
|
|
110
|
+
log('Redeeming ymaxControl invitation', id);
|
|
111
|
+
|
|
112
|
+
/** @type {BridgeAction} */
|
|
113
|
+
const redeemAction = harden({
|
|
114
|
+
method: 'executeOffer',
|
|
115
|
+
offer: {
|
|
116
|
+
id,
|
|
117
|
+
invitationSpec: {
|
|
118
|
+
source: 'purse',
|
|
119
|
+
instance: postalServiceInstance,
|
|
120
|
+
description: `deliver ${YMAX_CONTROL_WALLET_KEY}`,
|
|
121
|
+
},
|
|
122
|
+
proposal: {},
|
|
123
|
+
saveResult: { name: YMAX_CONTROL_WALLET_KEY, overwrite: true },
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const tx = await sig.sendBridgeAction(redeemAction);
|
|
128
|
+
if (tx.code !== 0) {
|
|
129
|
+
throw Error(`Failed to redeem invitation: ${tx.rawLog}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const wup = walletUpdates(sig.query.getLastUpdate, {
|
|
133
|
+
setTimeout: globalThis.setTimeout,
|
|
134
|
+
log,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await wup.offerResult(id);
|
|
138
|
+
log('ymaxControl invitation redeemed successfully');
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Helper to create action IDs with a consistent format
|
|
143
|
+
*
|
|
144
|
+
* @param {string} description
|
|
145
|
+
* @param {() => string | number} [fresh]
|
|
146
|
+
* @returns {string}
|
|
147
|
+
*/
|
|
148
|
+
export const makeActionId = (description, fresh = () => Date.now()) => {
|
|
149
|
+
return `${description}.${fresh()}`;
|
|
150
|
+
};
|