@abtnode/core 1.8.68-beta-500af7e5 → 1.8.68
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/lib/blocklet/downloader/bundle-downloader.js +1 -6
- package/lib/blocklet/manager/disk.js +64 -158
- package/lib/blocklet/manager/helper/install-from-backup.js +4 -4
- package/lib/blocklet/storage/backup/blocklet-extras.js +19 -15
- package/lib/blocklet/storage/backup/spaces.js +11 -39
- package/lib/blocklet/storage/restore/base.js +9 -10
- package/lib/blocklet/storage/restore/blocklet-extras.js +16 -21
- package/lib/blocklet/storage/restore/blocklets.js +2 -5
- package/lib/blocklet/storage/restore/spaces.js +38 -45
- package/lib/event.js +2 -18
- package/lib/index.js +3 -21
- package/lib/router/helper.js +5 -7
- package/lib/router/index.js +0 -31
- package/lib/states/audit-log.js +3 -5
- package/lib/states/blocklet.js +5 -21
- package/lib/states/node.js +2 -5
- package/lib/util/blocklet.js +19 -111
- package/lib/util/index.js +0 -24
- package/lib/validators/node.js +0 -1
- package/lib/webhook/index.js +1 -1
- package/package.json +27 -26
- /package/lib/{util/queue.js → queue.js} +0 -0
|
@@ -2,7 +2,6 @@ const { EventEmitter } = require('events');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const { fileURLToPath } = require('url');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const pick = require('lodash/pick');
|
|
6
5
|
const cloneDeep = require('lodash/cloneDeep');
|
|
7
6
|
const { toBase58 } = require('@ocap/util');
|
|
8
7
|
|
|
@@ -162,11 +161,7 @@ class BundleDownloader extends EventEmitter {
|
|
|
162
161
|
}
|
|
163
162
|
ctrlStore[rootDid].set(did, cancelCtrl);
|
|
164
163
|
|
|
165
|
-
const headers =
|
|
166
|
-
'x-server-did',
|
|
167
|
-
'x-server-public-key',
|
|
168
|
-
'x-server-signature',
|
|
169
|
-
]);
|
|
164
|
+
const headers = context.headers ? cloneDeep(context.headers) : {};
|
|
170
165
|
const exist = (context.downloadTokenList || []).find((x) => x.did === did);
|
|
171
166
|
if (exist) {
|
|
172
167
|
headers['x-download-token'] = exist.token;
|
|
@@ -4,7 +4,6 @@ const fs = require('fs-extra');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const flat = require('flat');
|
|
6
6
|
const get = require('lodash/get');
|
|
7
|
-
const merge = require('lodash/merge');
|
|
8
7
|
const pick = require('lodash/pick');
|
|
9
8
|
const cloneDeep = require('lodash/cloneDeep');
|
|
10
9
|
const semver = require('semver');
|
|
@@ -16,18 +15,20 @@ const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
|
16
15
|
const didDocument = require('@abtnode/util/lib/did-document');
|
|
17
16
|
const { sign } = require('@arcblock/jwt');
|
|
18
17
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
18
|
+
const { verifyPresentation } = require('@arcblock/vc');
|
|
19
19
|
const { toSvg: createDidLogo } =
|
|
20
20
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
21
21
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
22
22
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
23
23
|
|
|
24
24
|
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
25
|
+
const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
|
|
25
26
|
const {
|
|
27
|
+
VC_TYPE_BLOCKLET_PURCHASE,
|
|
26
28
|
WHO_CAN_ACCESS,
|
|
27
29
|
SERVER_ROLES,
|
|
28
30
|
WHO_CAN_ACCESS_PREFIX_ROLES,
|
|
29
31
|
BLOCKLET_INSTALL_TYPE,
|
|
30
|
-
NODE_MODES,
|
|
31
32
|
} = require('@abtnode/constant');
|
|
32
33
|
|
|
33
34
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
@@ -48,7 +49,7 @@ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-
|
|
|
48
49
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
49
50
|
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
50
51
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
51
|
-
const { titleSchema,
|
|
52
|
+
const { titleSchema, mountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
|
|
52
53
|
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
53
54
|
const Lock = require('@abtnode/util/lib/lock');
|
|
54
55
|
|
|
@@ -110,10 +111,6 @@ const {
|
|
|
110
111
|
validateAppConfig,
|
|
111
112
|
checkDuplicateAppSk,
|
|
112
113
|
checkDuplicateMountPoint,
|
|
113
|
-
validateStore,
|
|
114
|
-
validateInServerless,
|
|
115
|
-
isRotatingAppSk,
|
|
116
|
-
isRotatingAppDid,
|
|
117
114
|
} = require('../../util/blocklet');
|
|
118
115
|
const StoreUtil = require('../../util/store');
|
|
119
116
|
const states = require('../../states');
|
|
@@ -196,14 +193,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
196
193
|
/**
|
|
197
194
|
* @param {*} dataDirs generate by ../../util:getDataDirs
|
|
198
195
|
*/
|
|
199
|
-
constructor({ dataDirs, startQueue, installQueue,
|
|
196
|
+
constructor({ dataDirs, startQueue, installQueue, daemon = false, teamManager }) {
|
|
200
197
|
super();
|
|
201
198
|
|
|
202
199
|
this.dataDirs = dataDirs;
|
|
203
200
|
this.installDir = dataDirs.blocklets;
|
|
204
201
|
this.startQueue = startQueue;
|
|
205
202
|
this.installQueue = installQueue;
|
|
206
|
-
this.backupQueue = backupQueue;
|
|
207
203
|
this.teamManager = teamManager;
|
|
208
204
|
|
|
209
205
|
// cached installed blocklets for performance
|
|
@@ -246,8 +242,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
246
242
|
* did: string;
|
|
247
243
|
* title: string;
|
|
248
244
|
* description: string;
|
|
249
|
-
* storeUrl: string;
|
|
250
|
-
* appSk: string;
|
|
251
245
|
* sync: boolean = false; // download synchronously, not use queue
|
|
252
246
|
* delay: number; // push download task to queue after a delay
|
|
253
247
|
* downloadTokenList: Array<{did: string, token: string}>;
|
|
@@ -270,7 +264,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
270
264
|
|
|
271
265
|
const info = await states.node.read();
|
|
272
266
|
|
|
273
|
-
// Note: if you added new header here, please change core/state/lib/blocklet/downloader/bundle-downloader.js to use that header
|
|
274
267
|
context.headers = Object.assign(context?.headers || {}, {
|
|
275
268
|
'x-server-did': info.did,
|
|
276
269
|
'x-server-public-key': info.pk,
|
|
@@ -285,31 +278,28 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
285
278
|
context.startImmediately = !!params.startImmediately;
|
|
286
279
|
}
|
|
287
280
|
|
|
288
|
-
const { appSk } = params;
|
|
289
|
-
|
|
290
281
|
if (type === BLOCKLET_INSTALL_TYPE.URL) {
|
|
291
282
|
const { url, controller, sync, delay } = params;
|
|
292
|
-
return this._installFromUrl({ url, controller, sync, delay
|
|
283
|
+
return this._installFromUrl({ url, controller, sync, delay }, context);
|
|
293
284
|
}
|
|
294
285
|
|
|
295
286
|
if (type === BLOCKLET_INSTALL_TYPE.UPLOAD) {
|
|
296
287
|
const { file, did, diffVersion, deleteSet } = params;
|
|
297
|
-
return this._installFromUpload({ file, did, diffVersion, deleteSet,
|
|
288
|
+
return this._installFromUpload({ file, did, diffVersion, deleteSet, context });
|
|
298
289
|
}
|
|
299
290
|
|
|
300
291
|
if (type === BLOCKLET_INSTALL_TYPE.STORE) {
|
|
301
292
|
const { did, controller, sync, delay, storeUrl } = params;
|
|
302
|
-
return this._installFromStore({ did, controller, sync, delay, storeUrl
|
|
293
|
+
return this._installFromStore({ did, controller, sync, delay, storeUrl }, context);
|
|
303
294
|
}
|
|
304
295
|
|
|
305
296
|
if (type === BLOCKLET_INSTALL_TYPE.CREATE) {
|
|
306
|
-
|
|
307
|
-
return this._installFromCreate({ title, description, appSk }, context);
|
|
297
|
+
return this._installFromCreate({ title: params.title, description: params.description }, context);
|
|
308
298
|
}
|
|
309
299
|
|
|
310
300
|
if (type === BLOCKLET_INSTALL_TYPE.RESTORE) {
|
|
311
|
-
const { url } = params;
|
|
312
|
-
return this._installFromBackup({ url,
|
|
301
|
+
const { url, blockletSecretKey } = params;
|
|
302
|
+
return this._installFromBackup({ url, blockletSecretKey }, context);
|
|
313
303
|
}
|
|
314
304
|
|
|
315
305
|
// should not be here
|
|
@@ -353,16 +343,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
353
343
|
},
|
|
354
344
|
context = {}
|
|
355
345
|
) {
|
|
356
|
-
const mountPoint = await
|
|
346
|
+
const mountPoint = await mountPointSchema.validateAsync(tmpMountPoint);
|
|
357
347
|
logger.debug('start install component', { rootDid, mountPoint, url });
|
|
358
348
|
|
|
359
349
|
if (file) {
|
|
360
|
-
// TODO: 如何触发这种场景?
|
|
361
|
-
const info = await states.node.read();
|
|
362
|
-
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
363
|
-
throw new Error("Can't install component in serverless-mode server via upload");
|
|
364
|
-
}
|
|
365
|
-
|
|
366
350
|
return this._installComponentFromUpload({
|
|
367
351
|
rootDid,
|
|
368
352
|
mountPoint,
|
|
@@ -376,11 +360,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
376
360
|
}
|
|
377
361
|
|
|
378
362
|
if (url) {
|
|
379
|
-
const info = await states.node.read();
|
|
380
|
-
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
381
|
-
validateStore(info, url);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
363
|
return this._installComponentFromUrl({
|
|
385
364
|
rootDid,
|
|
386
365
|
mountPoint,
|
|
@@ -452,6 +431,25 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
452
431
|
return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
|
|
453
432
|
}
|
|
454
433
|
|
|
434
|
+
async installBlockletFromVc({ vcPresentation, challenge }, context) {
|
|
435
|
+
logger.info('Install from vc');
|
|
436
|
+
const vc = getVcFromPresentation(vcPresentation);
|
|
437
|
+
|
|
438
|
+
// FIXME: 这里的 trustedIssuers 相当于相信任何 VC,需要想更安全的方法
|
|
439
|
+
verifyPresentation({ presentation: vcPresentation, trustedIssuers: [get(vc, 'issuer.id')], challenge });
|
|
440
|
+
|
|
441
|
+
if (!vc.type.includes(VC_TYPE_BLOCKLET_PURCHASE)) {
|
|
442
|
+
throw new Error(`Expect ${VC_TYPE_BLOCKLET_PURCHASE} VC type`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const blockletUrl = get(vc, 'credentialSubject.purchased.blocklet.url');
|
|
446
|
+
const urlObject = new URL(blockletUrl);
|
|
447
|
+
const did = get(vc, 'credentialSubject.purchased.blocklet.id');
|
|
448
|
+
const registry = urlObject.origin;
|
|
449
|
+
|
|
450
|
+
return this._installFromStore({ did, registry }, context);
|
|
451
|
+
}
|
|
452
|
+
|
|
455
453
|
async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
|
|
456
454
|
logger.info('start blocklet', { did });
|
|
457
455
|
// should check blocklet integrity
|
|
@@ -610,67 +608,32 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
610
608
|
}
|
|
611
609
|
|
|
612
610
|
/**
|
|
613
|
-
*
|
|
614
|
-
*
|
|
611
|
+
*
|
|
612
|
+
*
|
|
613
|
+
* @param {import('@abtnode/client').BackupToSpacesParams} input
|
|
615
614
|
* @memberof BlockletManager
|
|
616
615
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
// add to queue
|
|
620
|
-
// const ticket = this.backupQueue.push({
|
|
621
|
-
// entity: 'blocklet',
|
|
622
|
-
// action: 'backup-to-space',
|
|
623
|
-
// did,
|
|
624
|
-
// context,
|
|
625
|
-
// });
|
|
626
|
-
|
|
627
|
-
// ticket.on('failed', async (err) => {
|
|
628
|
-
// logger.error('backup failed', { entity: 'blocklet', did, error: err });
|
|
629
|
-
// this.emit('blocklet.backup.failed', { did, err });
|
|
630
|
-
// this._createNotification(did, {
|
|
631
|
-
// title: 'Blocklet Backup Failed',
|
|
632
|
-
// description: `Blocklet backup failed with error: ${err.message || 'queue exception'}`,
|
|
633
|
-
// entityType: 'blocklet',
|
|
634
|
-
// entityId: did,
|
|
635
|
-
// severity: 'error',
|
|
636
|
-
// });
|
|
637
|
-
// });
|
|
638
|
-
|
|
639
|
-
const userDid = context.user.did;
|
|
640
|
-
const { referrer } = context;
|
|
641
|
-
|
|
642
|
-
// FIXME: @yejianchao did should be renamed to appDid
|
|
643
|
-
const spacesBackup = new SpacesBackup({ did, event: this, userDid, referrer });
|
|
644
|
-
this.emit(BlockletEvents.backupProgress, { did, message: 'Start backup...', progress: 10 });
|
|
616
|
+
async backupToSpaces(input) {
|
|
617
|
+
const spacesBackup = new SpacesBackup(input);
|
|
645
618
|
await spacesBackup.backup();
|
|
646
|
-
this.emit(BlockletEvents.backupProgress, { did, completed: true, progress: 100 });
|
|
647
619
|
}
|
|
648
620
|
|
|
649
621
|
/**
|
|
650
|
-
*
|
|
651
|
-
*
|
|
622
|
+
*
|
|
623
|
+
*
|
|
652
624
|
* @param {import('@abtnode/client').RequestRestoreFromSpacesInput} input
|
|
653
625
|
* @memberof BlockletManager
|
|
654
626
|
*/
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Start restore...' });
|
|
659
|
-
|
|
660
|
-
const userDid = context.user.did;
|
|
661
|
-
const { referrer } = context;
|
|
627
|
+
async restoreFromSpaces(input) {
|
|
628
|
+
const spacesRestore = new SpacesRestore(input);
|
|
629
|
+
await spacesRestore.restore();
|
|
662
630
|
|
|
663
|
-
|
|
664
|
-
const params = await spacesRestore.restore();
|
|
665
|
-
|
|
666
|
-
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Installing blocklet...' });
|
|
631
|
+
// FIXME: 需要改成队列执行,本次失败,下次还需要重试,页面刷新后也能知道最新的状态
|
|
667
632
|
await this._installFromBackup({
|
|
668
633
|
url: `file://${spacesRestore.blockletRestoreDir}`,
|
|
634
|
+
blockletSecretKey: spacesRestore.blockletWallet.secretKey,
|
|
669
635
|
moveDir: true,
|
|
670
|
-
...merge(...params),
|
|
671
636
|
});
|
|
672
|
-
|
|
673
|
-
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, completed: true });
|
|
674
637
|
}
|
|
675
638
|
|
|
676
639
|
async restart({ did }, context) {
|
|
@@ -1040,9 +1003,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1040
1003
|
return blocklets;
|
|
1041
1004
|
}
|
|
1042
1005
|
|
|
1043
|
-
// CAUTION: this method currently only support config by blocklet.meta.did
|
|
1044
1006
|
// eslint-disable-next-line no-unused-vars
|
|
1045
|
-
async config({ did, configs: newConfigs, skipHook
|
|
1007
|
+
async config({ did, configs: newConfigs, skipHook }, context) {
|
|
1046
1008
|
if (!Array.isArray(newConfigs)) {
|
|
1047
1009
|
throw new Error('configs list is not an array');
|
|
1048
1010
|
}
|
|
@@ -1072,7 +1034,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1072
1034
|
throw new Error(`Cannot set ${x.key} to child blocklet`);
|
|
1073
1035
|
}
|
|
1074
1036
|
|
|
1075
|
-
await validateAppConfig(x, states);
|
|
1037
|
+
await validateAppConfig(x, rootDid, states);
|
|
1076
1038
|
} else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
|
|
1077
1039
|
if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
|
|
1078
1040
|
// forbid unknown format key
|
|
@@ -1095,26 +1057,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1095
1057
|
});
|
|
1096
1058
|
}
|
|
1097
1059
|
|
|
1098
|
-
const willAppSkChange = isRotatingAppSk(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1099
|
-
const willAppDidChange = isRotatingAppDid(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1100
|
-
|
|
1101
1060
|
// update db
|
|
1102
1061
|
await states.blockletExtras.setConfigs(dids, newConfigs);
|
|
1103
1062
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
const migratedFrom = blocklet.migratedFrom || [];
|
|
1108
|
-
await states.blocklet.updateBlocklet(rootDid, {
|
|
1109
|
-
migratedFrom: [
|
|
1110
|
-
...migratedFrom,
|
|
1111
|
-
{ appSk: wallet.secretKey, appDid: wallet.address, at: new Date().toISOString() },
|
|
1112
|
-
],
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1063
|
+
const isSkOrWalletTypeChanged = newConfigs.find((item) =>
|
|
1064
|
+
[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE].includes(item.key)
|
|
1065
|
+
);
|
|
1115
1066
|
|
|
1116
|
-
|
|
1117
|
-
if (willAppDidChange && !skipDidDocument) {
|
|
1067
|
+
if (isSkOrWalletTypeChanged) {
|
|
1118
1068
|
await this._updateDidDocument(blocklet);
|
|
1119
1069
|
}
|
|
1120
1070
|
|
|
@@ -1248,7 +1198,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1248
1198
|
}
|
|
1249
1199
|
|
|
1250
1200
|
async updateComponentMountPoint({ did, rootDid: inputRootDid, mountPoint: tmpMountPoint }, context) {
|
|
1251
|
-
const mountPoint = await
|
|
1201
|
+
const mountPoint = await mountPointSchema.validateAsync(tmpMountPoint);
|
|
1252
1202
|
|
|
1253
1203
|
const blocklet = await states.blocklet.getBlocklet(inputRootDid);
|
|
1254
1204
|
|
|
@@ -1729,13 +1679,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1729
1679
|
* /blocklet.json
|
|
1730
1680
|
* /blocklet_extras.json
|
|
1731
1681
|
* @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
|
|
1732
|
-
* @param {{ url: string,
|
|
1682
|
+
* @param {{ url: string, blockletSecretKey: string, moveDir: boolean}} [{ url }={}]
|
|
1733
1683
|
* @param {Record<string, string>} [context={}]
|
|
1734
1684
|
* @return {Promise<any>}
|
|
1735
1685
|
* @memberof BlockletManager
|
|
1736
1686
|
*/
|
|
1737
|
-
async _installFromBackup({ url,
|
|
1738
|
-
return installFromBackup({ url,
|
|
1687
|
+
async _installFromBackup({ url, blockletSecretKey, moveDir } = {}, context = {}) {
|
|
1688
|
+
return installFromBackup({ url, blockletSecretKey, moveDir, context, manager: this, states });
|
|
1739
1689
|
}
|
|
1740
1690
|
|
|
1741
1691
|
async ensureBlocklet(did, opts = {}) {
|
|
@@ -2117,14 +2067,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2117
2067
|
// put BLOCKLET_APP_ID at root level for indexing
|
|
2118
2068
|
blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
|
|
2119
2069
|
|
|
2120
|
-
if (!blocklet.migratedFrom) {
|
|
2121
|
-
blocklet.migratedFrom = [];
|
|
2122
|
-
}
|
|
2123
|
-
// This can only be set once, can be used for indexing, will not change ever
|
|
2124
|
-
if (!blocklet.appPid) {
|
|
2125
|
-
blocklet.appPid = appSystemEnvironments.BLOCKLET_APP_PID;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
2070
|
// update state to db
|
|
2129
2071
|
return states.blocklet.updateBlocklet(did, blocklet);
|
|
2130
2072
|
}
|
|
@@ -2142,18 +2084,17 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2142
2084
|
*
|
|
2143
2085
|
* @param {{
|
|
2144
2086
|
* did: string;
|
|
2087
|
+
* registry: string;
|
|
2145
2088
|
* sync: boolean;
|
|
2146
2089
|
* delay: number;
|
|
2147
2090
|
* controller: Controller;
|
|
2148
|
-
* appSk: string;
|
|
2149
|
-
* storeUrl: string;
|
|
2150
2091
|
* }} params
|
|
2151
2092
|
* @param {*} context
|
|
2152
2093
|
* @return {*}
|
|
2153
2094
|
* @memberof BlockletManager
|
|
2154
2095
|
*/
|
|
2155
2096
|
async _installFromStore(params, context) {
|
|
2156
|
-
const { did, storeUrl, sync, delay, controller
|
|
2097
|
+
const { did, storeUrl, sync, delay, controller } = params;
|
|
2157
2098
|
|
|
2158
2099
|
logger.debug('start install blocklet', { did });
|
|
2159
2100
|
if (!isValidDid(did)) {
|
|
@@ -2197,7 +2138,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2197
2138
|
sync,
|
|
2198
2139
|
delay,
|
|
2199
2140
|
controller,
|
|
2200
|
-
appSk,
|
|
2201
2141
|
context,
|
|
2202
2142
|
});
|
|
2203
2143
|
}
|
|
@@ -2214,14 +2154,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2214
2154
|
* sync: boolean;
|
|
2215
2155
|
* delay: number;
|
|
2216
2156
|
* controller: Controller;
|
|
2217
|
-
* appSk: string;
|
|
2218
2157
|
* }} params
|
|
2219
2158
|
* @param {{}} context
|
|
2220
2159
|
* @return {*}
|
|
2221
2160
|
* @memberof BlockletManager
|
|
2222
2161
|
*/
|
|
2223
2162
|
async _installFromUrl(params, context) {
|
|
2224
|
-
const { url, sync, delay, controller
|
|
2163
|
+
const { url, sync, delay, controller } = params;
|
|
2225
2164
|
|
|
2226
2165
|
logger.debug('start install blocklet', { url });
|
|
2227
2166
|
|
|
@@ -2235,18 +2174,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2235
2174
|
|
|
2236
2175
|
// install from store if url is a store url
|
|
2237
2176
|
const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
|
|
2238
|
-
|
|
2239
|
-
const nodeInfo = await states.node.read();
|
|
2240
|
-
|
|
2241
|
-
await validateStore(nodeInfo, registryUrl);
|
|
2242
|
-
|
|
2243
2177
|
if (inStore) {
|
|
2244
2178
|
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
2245
2179
|
if (exist) {
|
|
2246
2180
|
return this.upgrade({ did: blockletDid, storeUrl: registryUrl, sync, delay }, context);
|
|
2247
2181
|
}
|
|
2248
2182
|
|
|
2249
|
-
return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay
|
|
2183
|
+
return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay }, context);
|
|
2250
2184
|
}
|
|
2251
2185
|
|
|
2252
2186
|
const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
|
|
@@ -2272,7 +2206,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2272
2206
|
sync,
|
|
2273
2207
|
delay,
|
|
2274
2208
|
controller,
|
|
2275
|
-
appSk,
|
|
2276
2209
|
context,
|
|
2277
2210
|
});
|
|
2278
2211
|
}
|
|
@@ -2377,7 +2310,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2377
2310
|
);
|
|
2378
2311
|
}
|
|
2379
2312
|
|
|
2380
|
-
async _installFromCreate({ title, description
|
|
2313
|
+
async _installFromCreate({ title, description }, context = {}) {
|
|
2381
2314
|
logger.debug('create blocklet', { title, description });
|
|
2382
2315
|
|
|
2383
2316
|
await joi.string().label('title').max(20).required().validateAsync(title);
|
|
@@ -2405,9 +2338,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2405
2338
|
};
|
|
2406
2339
|
const meta = validateMeta(rawMeta);
|
|
2407
2340
|
|
|
2408
|
-
await states.blocklet.addBlocklet({
|
|
2341
|
+
await states.blocklet.addBlocklet({
|
|
2342
|
+
meta,
|
|
2343
|
+
source: BlockletSource.custom,
|
|
2344
|
+
});
|
|
2409
2345
|
await this._setConfigsFromMeta(did);
|
|
2410
|
-
await this._setAppSk(did, appSk);
|
|
2411
2346
|
|
|
2412
2347
|
// check duplicate appSk
|
|
2413
2348
|
await checkDuplicateAppSk({ did, states });
|
|
@@ -2447,7 +2382,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2447
2382
|
return { cwd, tarFile };
|
|
2448
2383
|
}
|
|
2449
2384
|
|
|
2450
|
-
async _installFromUpload({ file, did, diffVersion, deleteSet,
|
|
2385
|
+
async _installFromUpload({ file, did, diffVersion, deleteSet, context }) {
|
|
2451
2386
|
logger.info('install blocklet', { from: 'upload file' });
|
|
2452
2387
|
const { tarFile } = await this._downloadFromUpload(file);
|
|
2453
2388
|
|
|
@@ -2539,14 +2474,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2539
2474
|
source: BlockletSource.upload,
|
|
2540
2475
|
deployedFrom: `Upload by ${context.user.fullName}`,
|
|
2541
2476
|
children,
|
|
2542
|
-
externalSk: !!appSk,
|
|
2543
2477
|
});
|
|
2544
2478
|
|
|
2545
2479
|
const action = 'install';
|
|
2546
2480
|
const oldState = { extraState: oldExtraState };
|
|
2547
2481
|
try {
|
|
2548
2482
|
await this._setConfigsFromMeta(meta.did);
|
|
2549
|
-
await this._setAppSk(appSk);
|
|
2550
2483
|
await validateBlocklet(blocklet);
|
|
2551
2484
|
|
|
2552
2485
|
// check duplicate appSk
|
|
@@ -2843,7 +2776,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2843
2776
|
* meta: {}; // blocklet meta
|
|
2844
2777
|
* source: number; // example: BlockletSource.registry,
|
|
2845
2778
|
* deployedFrom: string;
|
|
2846
|
-
* appSk: string;
|
|
2847
2779
|
* context: {}
|
|
2848
2780
|
* sync: boolean = false;
|
|
2849
2781
|
* delay: number;
|
|
@@ -2853,15 +2785,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2853
2785
|
* @memberof BlockletManager
|
|
2854
2786
|
*/
|
|
2855
2787
|
async _install(params) {
|
|
2856
|
-
const { meta, source, deployedFrom, context, sync, delay, controller
|
|
2788
|
+
const { meta, source, deployedFrom, context, sync, delay, controller } = params;
|
|
2857
2789
|
|
|
2858
2790
|
validateBlockletMeta(meta, { ensureDist: true });
|
|
2859
2791
|
|
|
2860
|
-
const info = await states.node.read();
|
|
2861
|
-
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
2862
|
-
validateInServerless({ blockletMeta: meta });
|
|
2863
|
-
}
|
|
2864
|
-
|
|
2865
2792
|
const { name, did, version } = meta;
|
|
2866
2793
|
|
|
2867
2794
|
const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
|
|
@@ -2874,7 +2801,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2874
2801
|
source,
|
|
2875
2802
|
deployedFrom,
|
|
2876
2803
|
children,
|
|
2877
|
-
externalSk: !!appSk,
|
|
2878
2804
|
});
|
|
2879
2805
|
|
|
2880
2806
|
await validateBlocklet(blocklet);
|
|
@@ -2882,7 +2808,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2882
2808
|
await states.blockletExtras.addMeta({ did, meta: { did, name }, controller });
|
|
2883
2809
|
|
|
2884
2810
|
await this._setConfigsFromMeta(did);
|
|
2885
|
-
await this._setAppSk(did, appSk);
|
|
2886
2811
|
|
|
2887
2812
|
// check duplicate appSk
|
|
2888
2813
|
await checkDuplicateAppSk({ did, states });
|
|
@@ -3480,31 +3405,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3480
3405
|
}
|
|
3481
3406
|
}
|
|
3482
3407
|
|
|
3483
|
-
async _setAppSk(did, appSk, context) {
|
|
3484
|
-
if (process.env.NODE_ENV === 'production' && !appSk) {
|
|
3485
|
-
throw new Error(`appSk for blocklet ${did} is required`);
|
|
3486
|
-
}
|
|
3487
|
-
|
|
3488
|
-
if (appSk) {
|
|
3489
|
-
await this.config(
|
|
3490
|
-
{
|
|
3491
|
-
did,
|
|
3492
|
-
configs: [{ key: 'BLOCKLET_APP_SK', value: appSk, secure: true }],
|
|
3493
|
-
skipHook: true,
|
|
3494
|
-
skipDidDocument: true,
|
|
3495
|
-
},
|
|
3496
|
-
context
|
|
3497
|
-
);
|
|
3498
|
-
}
|
|
3499
|
-
}
|
|
3500
|
-
|
|
3501
3408
|
async _findNextCustomBlockletName(leftTimes = 10) {
|
|
3502
3409
|
if (leftTimes <= 0) {
|
|
3503
3410
|
throw new Error('Generate custom blocklet did too many times');
|
|
3504
3411
|
}
|
|
3505
3412
|
const number = await states.node.increaseCustomBlockletNumber();
|
|
3506
3413
|
const name = `custom-${number}`;
|
|
3507
|
-
// MEMO: 空壳 APP可以保留原有的 did 生成逻辑
|
|
3508
3414
|
const did = toBlockletDid(name);
|
|
3509
3415
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
3510
3416
|
if (blocklet) {
|
|
@@ -19,7 +19,7 @@ const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../
|
|
|
19
19
|
* }} param0
|
|
20
20
|
* @returns
|
|
21
21
|
*/
|
|
22
|
-
module.exports = async ({ url,
|
|
22
|
+
module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states, manager } = {}) => {
|
|
23
23
|
// TODO: support more url schema feature (http, did-spaces)
|
|
24
24
|
if (!url.startsWith('file://')) {
|
|
25
25
|
throw new Error('url must starts with file://');
|
|
@@ -71,17 +71,17 @@ module.exports = async ({ url, appSk, moveDir, context = {}, states, manager } =
|
|
|
71
71
|
throw new Error('blocklet is already exist');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (
|
|
74
|
+
if (blockletSecretKey) {
|
|
75
75
|
extra.configs = extra.configs || [];
|
|
76
76
|
const skConfig = extra.configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
77
77
|
if (skConfig) {
|
|
78
|
-
skConfig.value =
|
|
78
|
+
skConfig.value = blockletSecretKey;
|
|
79
79
|
skConfig.secure = true;
|
|
80
80
|
skConfig.shared = false;
|
|
81
81
|
} else {
|
|
82
82
|
extra.configs.push({
|
|
83
83
|
key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
|
|
84
|
-
value:
|
|
84
|
+
value: blockletSecretKey,
|
|
85
85
|
secure: true,
|
|
86
86
|
shared: false,
|
|
87
87
|
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const { removeSync, outputJsonSync, readFileSync } = require('fs-extra');
|
|
2
2
|
const { isEmpty, cloneDeep } = require('lodash');
|
|
3
3
|
const { join } = require('path');
|
|
4
|
-
const { Hasher } = require('@ocap/mcrypto');
|
|
5
|
-
const { toBuffer } = require('@ocap/util');
|
|
6
4
|
const security = require('@abtnode/util/lib/security');
|
|
5
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
7
6
|
const states = require('../../../states');
|
|
8
7
|
const { BaseBackup } = require('./base');
|
|
9
8
|
|
|
@@ -33,7 +32,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
33
32
|
throw new Error('blockletExtra cannot be empty');
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
return this.
|
|
35
|
+
return this.cleanDate(blockletExtra);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
@@ -43,7 +42,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
43
42
|
* @return {Promise<void>}
|
|
44
43
|
* @memberof BlockletExtrasBackup
|
|
45
44
|
*/
|
|
46
|
-
async
|
|
45
|
+
async cleanDate(blockletExtraInput) {
|
|
47
46
|
const blockletExtra = cloneDeep(blockletExtraInput);
|
|
48
47
|
|
|
49
48
|
const queue = [blockletExtra];
|
|
@@ -58,7 +57,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
// 加解密
|
|
61
|
-
this.
|
|
60
|
+
this.useBlockletEncryptConfigs(currentBlockletExtra.configs);
|
|
62
61
|
|
|
63
62
|
if (currentBlockletExtra?.children) {
|
|
64
63
|
queue.push(...currentBlockletExtra.children);
|
|
@@ -75,28 +74,33 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
75
74
|
* @return {void}
|
|
76
75
|
* @memberof BlockletExtrasBackup
|
|
77
76
|
*/
|
|
78
|
-
|
|
77
|
+
useBlockletEncryptConfigs(configs) {
|
|
79
78
|
if (isEmpty(configs)) {
|
|
80
79
|
return;
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
const { secretKey, address } = this.blockletWallet;
|
|
84
|
-
|
|
85
82
|
// 准备加解密所需的参数
|
|
86
83
|
// @see: https://github.com/ArcBlock/blocklet-server/blob/f561ba7290285f2e23dccb6d5323eb4d43c3cc3e/core/state/lib/index.js#L59
|
|
87
|
-
const
|
|
88
|
-
const ek = toBuffer(Hasher.SHA3.hash256(Buffer.concat([secretKey, address].map(toBuffer))));
|
|
84
|
+
const dek = readFileSync(join(this.serverDataDir, '.sock'));
|
|
89
85
|
|
|
90
86
|
for (const config of configs) {
|
|
91
|
-
|
|
87
|
+
// 置空 blocklet 的密钥,但是保留其他属性,这很重要
|
|
88
|
+
if (config.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
|
|
89
|
+
config.value = '';
|
|
90
|
+
} else if (config.secure) {
|
|
92
91
|
// secure 为 true 的配置才需要被加密保存上次到 did spaces
|
|
92
|
+
|
|
93
93
|
const encryptByServer = config.value;
|
|
94
94
|
// 先从 blocklet server 解密
|
|
95
95
|
// @see https://github.com/ArcBlock/blocklet-server/blob/f40338168a66893f325464cea79ae54c43f623b1/core/state/lib/blocklet/extras.js#L139
|
|
96
|
-
const
|
|
97
|
-
// 再用 blocklet
|
|
98
|
-
const
|
|
99
|
-
|
|
96
|
+
const decryptByServer = security.decrypt(encryptByServer, this.blocklet.meta.did, dek);
|
|
97
|
+
// 再用 blocklet secret 加密,然后才可以上传到 spaces
|
|
98
|
+
const encryptByBlocklet = security.encrypt(
|
|
99
|
+
decryptByServer,
|
|
100
|
+
this.blockletWallet.address,
|
|
101
|
+
Buffer.from(this.blockletWallet.secretKey)
|
|
102
|
+
);
|
|
103
|
+
config.value = encryptByBlocklet;
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
}
|