@xyo-network/os-runtime 3.0.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/LICENSE +165 -0
- package/README.md +13 -0
- package/dist/neutral/index.d.ts +1656 -0
- package/dist/neutral/index.mjs +3432 -0
- package/dist/neutral/index.mjs.map +1 -0
- package/package.json +78 -0
- package/src/Caller.ts +221 -0
- package/src/DappCallerBase.ts +52 -0
- package/src/DefaultsQueries.ts +27 -0
- package/src/OsCallerBase.ts +55 -0
- package/src/PubSubBridgeCaller.ts +30 -0
- package/src/XyOs.ts +125 -0
- package/src/XyOsBase.ts +119 -0
- package/src/XyOsDapp.ts +90 -0
- package/src/access-interfaces/ValidDappAccessInterfaces.ts +8 -0
- package/src/access-interfaces/index.ts +2 -0
- package/src/access-interfaces/registered-names/helpers/AccessNodeQueries.ts +59 -0
- package/src/access-interfaces/registered-names/helpers/index.ts +2 -0
- package/src/access-interfaces/registered-names/helpers/resource/AbstractXnsCaller.ts +38 -0
- package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResource.ts +54 -0
- package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResourceQueries.ts +22 -0
- package/src/access-interfaces/registered-names/helpers/resource/index.ts +3 -0
- package/src/access-interfaces/registered-names/index.ts +1 -0
- package/src/adapter/Base.ts +96 -0
- package/src/adapter/Network.ts +31 -0
- package/src/adapter/Settings.ts +30 -0
- package/src/adapter/index.ts +2 -0
- package/src/adapters/OsPubSubBridgeNetwork.ts +10 -0
- package/src/adapters/OsSettings.ts +7 -0
- package/src/adapters/OsXyoPublicNetwork.ts +9 -0
- package/src/adapters/index.ts +3 -0
- package/src/classes/cache/RunningAccessDappCache.ts +21 -0
- package/src/classes/cache/RunningDappCache.ts +50 -0
- package/src/classes/cache/index.ts +2 -0
- package/src/classes/dapp/DefaultsResource.ts +65 -0
- package/src/classes/dapp/access/Caller.ts +73 -0
- package/src/classes/dapp/access/Queries.ts +38 -0
- package/src/classes/dapp/access/Resource.ts +63 -0
- package/src/classes/dapp/access/index.ts +3 -0
- package/src/classes/dapp/index.ts +2 -0
- package/src/classes/index.ts +8 -0
- package/src/classes/lib/DappCreatorParams.ts +16 -0
- package/src/classes/lib/Insertable.ts +14 -0
- package/src/classes/lib/index.ts +2 -0
- package/src/classes/menu/Caller.ts +76 -0
- package/src/classes/menu/Queries.ts +59 -0
- package/src/classes/menu/Resource.ts +103 -0
- package/src/classes/menu/index.ts +2 -0
- package/src/classes/node/Creator.ts +96 -0
- package/src/classes/node/DefaultPayloads/DappAccessPayloads.ts +17 -0
- package/src/classes/node/DefaultPayloads/DefaultPayloads.ts +33 -0
- package/src/classes/node/DefaultPayloads/NodeInfoPayload.ts +27 -0
- package/src/classes/node/DefaultPayloads/SigningKeyPayloads.ts +85 -0
- package/src/classes/node/DefaultPayloads/index.ts +1 -0
- package/src/classes/node/ExternalModulePermissions/ExternalModulePermissions.ts +47 -0
- package/src/classes/node/ExternalModulePermissions/index.ts +1 -0
- package/src/classes/node/createDappContext.ts +52 -0
- package/src/classes/node/index.ts +3 -0
- package/src/classes/registration/DappRegistrationService.ts +146 -0
- package/src/classes/registration/DappRegistry.ts +121 -0
- package/src/classes/registration/ValidateDappAccessDiviner/Config.ts +19 -0
- package/src/classes/registration/ValidateDappAccessDiviner/Diviner.ts +132 -0
- package/src/classes/registration/ValidateDappAccessDiviner/index.ts +2 -0
- package/src/classes/registration/index.ts +2 -0
- package/src/classes/settings/Caller.ts +76 -0
- package/src/classes/settings/CallerBase.ts +24 -0
- package/src/classes/settings/Resource.ts +79 -0
- package/src/classes/settings/SettingsQueries.ts +38 -0
- package/src/classes/settings/badge/Caller.ts +30 -0
- package/src/classes/settings/badge/Queries.ts +18 -0
- package/src/classes/settings/badge/Resource.ts +54 -0
- package/src/classes/settings/badge/index.ts +2 -0
- package/src/classes/settings/index.ts +5 -0
- package/src/classes/system/ManageSystemDapps.ts +131 -0
- package/src/classes/system/Queries.ts +69 -0
- package/src/classes/system/index.ts +1 -0
- package/src/event/bus/Connection.ts +31 -0
- package/src/event/bus/EventBus.ts +145 -0
- package/src/event/bus/PubSubConnection.ts +28 -0
- package/src/event/bus/index.ts +3 -0
- package/src/event/connections/DappAccessRequest.ts +11 -0
- package/src/event/connections/DappsReady.ts +9 -0
- package/src/event/connections/ExposeDappRequest.ts +11 -0
- package/src/event/connections/OsPubSubNetworkReady.ts +12 -0
- package/src/event/connections/OsSettingsReady.ts +12 -0
- package/src/event/connections/OsXyoPublicReady.ts +12 -0
- package/src/event/connections/index.ts +6 -0
- package/src/event/index.ts +2 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/monitor/XyOsMonitor.ts +52 -0
- package/src/helpers/monitor/index.ts +2 -0
- package/src/helpers/monitor/types.ts +5 -0
- package/src/index.ts +22 -0
- package/src/intent/Caller.ts +72 -0
- package/src/intent/Resource.ts +66 -0
- package/src/intent/index.ts +2 -0
- package/src/lib/ExternalStore.ts +7 -0
- package/src/lib/Listener.ts +1 -0
- package/src/lib/ModuleAccountPaths.ts +29 -0
- package/src/lib/ModuleNames.ts +3 -0
- package/src/lib/NameTransforms.ts +31 -0
- package/src/lib/PayloadStore.ts +98 -0
- package/src/lib/ResourceStores.ts +7 -0
- package/src/lib/index.ts +10 -0
- package/src/lib/initializeXns.ts +16 -0
- package/src/lib/isPayload.ts +24 -0
- package/src/lib/tokenPlacesSplit.ts +17 -0
- package/src/loadOsNode.ts +43 -0
- package/src/manifest/ManifestReplaceableTokens.ts +17 -0
- package/src/manifest/index.ts +2 -0
- package/src/manifest/os-node.manifest.json +132 -0
- package/src/manifests/dapp-window.manifest.json +36 -0
- package/src/manifests/index.ts +1 -0
- package/src/profileModuleEvents.ts +43 -0
- package/src/stack/Base.ts +153 -0
- package/src/stack/Manager.ts +48 -0
- package/src/stack/Map.ts +20 -0
- package/src/stack/OsPubSubNetworkStack.ts +70 -0
- package/src/stack/OsSettingsStack.ts +24 -0
- package/src/stack/XyoPublicNetworkStack.ts +32 -0
- package/src/stack/index.ts +6 -0
- package/src/types/global.d.ts +9 -0
- package/src/types/images.d.ts +5 -0
- package/src/utils/buildWalletSeedPhrasePayload.ts +41 -0
- package/src/utils/getApiDomain.ts +14 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/renameObjKey.ts +22 -0
- package/src/wallet/DappSeedPhraseRepository.ts +95 -0
- package/src/wallet/SeedPhraseRepository.ts +32 -0
- package/src/wallet/index.ts +2 -0
- package/typedoc.json +5 -0
- package/xy.config.ts +10 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { asArchivistInstance } from '@xyo-network/archivist-model'
|
|
3
|
+
import type { PackageManifestPayload } from '@xyo-network/manifest'
|
|
4
|
+
import { PackageManifestPayloadSchema } from '@xyo-network/manifest'
|
|
5
|
+
import type { ManifestWrapper } from '@xyo-network/manifest-wrapper'
|
|
6
|
+
import type { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
7
|
+
import type { NodeInstance } from '@xyo-network/node-model'
|
|
8
|
+
import type { DappId, DappPackageManifestPayload, ExternalModule, XyOsContext } from '@xyo-network/os-model'
|
|
9
|
+
import type { WalletInstance } from '@xyo-network/wallet-model'
|
|
10
|
+
|
|
11
|
+
import { DappCallerBase } from '../../DappCallerBase.ts'
|
|
12
|
+
import { ReplaceManifestTokens } from '../../manifest/index.ts'
|
|
13
|
+
import type { DappNodesCreatorParams } from '../lib/index.ts'
|
|
14
|
+
import { DefaultPayloads } from './DefaultPayloads/index.ts'
|
|
15
|
+
import { ExternalModulePermissions } from './ExternalModulePermissions/index.ts'
|
|
16
|
+
|
|
17
|
+
export type TargetDappManifestParams = ConstructorParameters<typeof ManifestWrapper<DappPackageManifestPayload>>
|
|
18
|
+
|
|
19
|
+
export class DappContextCreator {
|
|
20
|
+
// Designated offset path for the wallet used by windowed dapps. In the future, other offsets could be used for other dapp modes
|
|
21
|
+
private static DAPP_WINDOW_WALLET_PATH = '1'
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
private targetDappManifestParams: TargetDappManifestParams,
|
|
25
|
+
private dappId: DappId,
|
|
26
|
+
private context: XyOsContext,
|
|
27
|
+
private rootWallet: WalletInstance,
|
|
28
|
+
private externalPermissions?: ExternalModule[],
|
|
29
|
+
private sharedLocator?: ModuleFactoryLocator,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
static async create(params: DappNodesCreatorParams, xnsNodeUrl: string | undefined, xnsNetwork: string | undefined) {
|
|
33
|
+
const { config, locator, context, wallet } = params
|
|
34
|
+
const { payload, dappName, publicChildren, privateChildren } = config
|
|
35
|
+
|
|
36
|
+
const { external, manifestPayload } = this.parseDappPackageManifestPayload(payload, xnsNodeUrl, xnsNetwork)
|
|
37
|
+
console.debug('[DEBUG] dApp manifest', manifestPayload)
|
|
38
|
+
|
|
39
|
+
// derive a wallet for the dapp to use from the root wallet
|
|
40
|
+
const dappWallet = await wallet.derivePath(DappContextCreator.DAPP_WINDOW_WALLET_PATH)
|
|
41
|
+
const targetDappManifestParams: TargetDappManifestParams = [manifestPayload, dappWallet, locator, publicChildren, privateChildren]
|
|
42
|
+
|
|
43
|
+
const instance = new this(targetDappManifestParams, dappName, context, wallet, external?.modules, locator)
|
|
44
|
+
return instance
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Add default payloads to the archivist shared between the os and the dapp
|
|
48
|
+
static async primeDappArchivist(dappContext: XyOsContext, osContext: XyOsContext, dappName: DappId) {
|
|
49
|
+
const dappCaller = new DappCallerBase(dappContext)
|
|
50
|
+
const dappArchivist = await dappCaller.getDappArchivist()
|
|
51
|
+
const defaultPayloads = new DefaultPayloads(dappArchivist, osContext, dappName)
|
|
52
|
+
|
|
53
|
+
await defaultPayloads.insert()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Reset the dapp archivist to remove all payloads
|
|
57
|
+
static async resetDappArchivist(context: XyOsContext) {
|
|
58
|
+
const dappModule = await context.root.resolve('DappArchivist')
|
|
59
|
+
const dappArchivist = assertEx(asArchivistInstance(dappModule), () => 'DappArchivist not found')
|
|
60
|
+
|
|
61
|
+
await dappArchivist.clear()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private static parseDappPackageManifestPayload(payload: DappPackageManifestPayload, xnsNodeUrl: string | undefined, xnsNetwork: string | undefined) {
|
|
65
|
+
// extract external permissions from package manifest payload
|
|
66
|
+
const { external, ...manifestWithoutExternals } = payload
|
|
67
|
+
const manifestPayloadRaw: PackageManifestPayload = {
|
|
68
|
+
...manifestWithoutExternals,
|
|
69
|
+
schema: PackageManifestPayloadSchema,
|
|
70
|
+
}
|
|
71
|
+
const manifestPayload = ReplaceManifestTokens(manifestPayloadRaw, xnsNodeUrl, xnsNetwork)
|
|
72
|
+
return { external, manifestPayload }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async loadDappContext(context: XyOsContext): Promise<XyOsContext> {
|
|
76
|
+
console.log('DappNodesCreator:loadDappContext', this.dappId)
|
|
77
|
+
// build nodes and attach to dappNodes to dappWindowNode
|
|
78
|
+
const dapp
|
|
79
|
+
= (await context.dappByName(this.dappId))
|
|
80
|
+
?? (await (async () => {
|
|
81
|
+
const dapp = await context.buildDapp(this.targetDappManifestParams[0], this.dappId)
|
|
82
|
+
await dapp.boot(this.rootWallet, this.sharedLocator)
|
|
83
|
+
return dapp
|
|
84
|
+
})())
|
|
85
|
+
await this.handleExternalModulePermissions(dapp.root)
|
|
86
|
+
await DappContextCreator.primeDappArchivist(dapp, this.context, this.dappId)
|
|
87
|
+
|
|
88
|
+
return dapp
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// try to satisfy the external permissions requested by a dapp
|
|
92
|
+
private async handleExternalModulePermissions(dappWindowNode: NodeInstance) {
|
|
93
|
+
const externalModulePermissions = new ExternalModulePermissions(this.context, dappWindowNode, this.dappId, this.externalPermissions)
|
|
94
|
+
await externalModulePermissions.permit()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DappName, XyOsContext } from '@xyo-network/os-model'
|
|
2
|
+
|
|
3
|
+
import { OsCallerBase } from '../../../OsCallerBase.ts'
|
|
4
|
+
import type { DefaultPayloadsInsertable } from '../../lib/index.ts'
|
|
5
|
+
|
|
6
|
+
export class DappAccessPayloads implements DefaultPayloadsInsertable {
|
|
7
|
+
constructor(
|
|
8
|
+
private context: XyOsContext,
|
|
9
|
+
_dappName: DappName,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
async payloads() {
|
|
13
|
+
const osCaller = new OsCallerBase(this.context)
|
|
14
|
+
const archivist = await osCaller.getRegisteredDappInterfacesArchivist()
|
|
15
|
+
return await archivist.all()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ArchivistInstance } from '@xyo-network/archivist-model'
|
|
2
|
+
import type { DappName, XyOsContext } from '@xyo-network/os-model'
|
|
3
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
4
|
+
import type { Payload } from '@xyo-network/payload-model'
|
|
5
|
+
|
|
6
|
+
import type { InsertableConstructor } from '../../lib/index.ts'
|
|
7
|
+
import { createInsertable } from '../../lib/index.ts'
|
|
8
|
+
import { DappAccessPayloads } from './DappAccessPayloads.ts'
|
|
9
|
+
import { NodeInfoPayload } from './NodeInfoPayload.ts'
|
|
10
|
+
import { SigningKeyPayloads } from './SigningKeyPayloads.ts'
|
|
11
|
+
|
|
12
|
+
export class DefaultPayloads {
|
|
13
|
+
constructor(
|
|
14
|
+
private dappArchivist: ArchivistInstance,
|
|
15
|
+
private xyOs: XyOsContext,
|
|
16
|
+
private dappName: DappName,
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
async insert() {
|
|
20
|
+
const insertables: InsertableConstructor[] = [NodeInfoPayload, SigningKeyPayloads, DappAccessPayloads]
|
|
21
|
+
const insertPayloads: Payload[] = []
|
|
22
|
+
for (const insertable of insertables) {
|
|
23
|
+
const classInstance = createInsertable(insertable, this.xyOs, this.dappName)
|
|
24
|
+
const payloads = await classInstance.payloads()
|
|
25
|
+
insertPayloads.push(...payloads)
|
|
26
|
+
}
|
|
27
|
+
for (const payload of insertPayloads) {
|
|
28
|
+
const [existing] = await this.dappArchivist.get([await PayloadBuilder.dataHash(payload)])
|
|
29
|
+
if (existing) continue
|
|
30
|
+
await this.dappArchivist.insert([payload])
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DappName, NodeOsInfo, XyOsContext } from '@xyo-network/os-model'
|
|
2
|
+
import { NodeOsInfoSchema } from '@xyo-network/os-model'
|
|
3
|
+
|
|
4
|
+
import type { DefaultPayloadsInsertable } from '../../lib/index.ts'
|
|
5
|
+
|
|
6
|
+
export class NodeInfoPayload implements DefaultPayloadsInsertable {
|
|
7
|
+
constructor(
|
|
8
|
+
private _context: XyOsContext,
|
|
9
|
+
_dappName: DappName,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
async payloads() {
|
|
13
|
+
const exposedNode = this._context.exposedNode
|
|
14
|
+
if (exposedNode) {
|
|
15
|
+
console.debug('[DEBUG]', 'Found Exposed Node Address', exposedNode?.address)
|
|
16
|
+
} else {
|
|
17
|
+
console.error('exposedNode not found')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
await Promise.resolve()
|
|
21
|
+
const nodeOsInfo: NodeOsInfo = {
|
|
22
|
+
publicAddress: exposedNode?.address ?? '',
|
|
23
|
+
schema: NodeOsInfoSchema,
|
|
24
|
+
}
|
|
25
|
+
return [nodeOsInfo]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { hexFrom } from '@xylabs/hex'
|
|
3
|
+
import { HDWallet } from '@xyo-network/account'
|
|
4
|
+
import type { DappName, XyOsContext } from '@xyo-network/os-model'
|
|
5
|
+
import { mnemonicArrayToString, SigningKeySchema } from '@xyo-network/os-model'
|
|
6
|
+
import type { Payload } from '@xyo-network/payload-model'
|
|
7
|
+
import { isAnyPayload } from '@xyo-network/payload-model'
|
|
8
|
+
import type { WalletInstance } from '@xyo-network/wallet-model'
|
|
9
|
+
|
|
10
|
+
import type { DefaultPayloadsInsertable } from '../../lib/index.ts'
|
|
11
|
+
import { OsSettingsCaller } from '../../settings/index.ts'
|
|
12
|
+
|
|
13
|
+
const DappsWithAdditionalPayloads = new Set(['Profile'])
|
|
14
|
+
|
|
15
|
+
const DefaultName: DappName = 'Name Service' as const
|
|
16
|
+
|
|
17
|
+
const DEFAULT_DAPP_ACCOUNT_PATHS: Record<string, string> = {
|
|
18
|
+
[DefaultName]: "1'/1",
|
|
19
|
+
} as const
|
|
20
|
+
|
|
21
|
+
// WIP - for now, dApps listed here can gain access to the signing key for other dapps
|
|
22
|
+
// In the future, this will be controlled by the user
|
|
23
|
+
const ADDITIONAL_DAPP_ACCOUNT_PATHS: Record<string, Record<string, string>> = {
|
|
24
|
+
['Profile']: {
|
|
25
|
+
[DefaultName]: DEFAULT_DAPP_ACCOUNT_PATHS[DefaultName],
|
|
26
|
+
},
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
export class SigningKeyPayloads implements DefaultPayloadsInsertable {
|
|
30
|
+
constructor(
|
|
31
|
+
private context: XyOsContext,
|
|
32
|
+
private dappName: DappName,
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
async payloads() {
|
|
36
|
+
const wallet = await this.resolveXyoWallet()
|
|
37
|
+
|
|
38
|
+
// get the signing key payloads for the default dapp account
|
|
39
|
+
const defaultDappAccountSigningKeyPayloads = await this.signingKeyPayload(wallet, this.dappName, DEFAULT_DAPP_ACCOUNT_PATHS[this.dappName])
|
|
40
|
+
|
|
41
|
+
// and any additional dapp accounts
|
|
42
|
+
const additionalDappAccountSigningKeyPayloads: (Payload | undefined)[] = []
|
|
43
|
+
for (const [dappName, accountPaths] of Object.entries(ADDITIONAL_DAPP_ACCOUNT_PATHS)) {
|
|
44
|
+
if (dappName !== this.dappName || !DappsWithAdditionalPayloads.has(dappName)) continue
|
|
45
|
+
|
|
46
|
+
for (const additionalDappAccountName of Object.keys(accountPaths)) {
|
|
47
|
+
const signingKeyPayload = await this.signingKeyPayload(wallet, additionalDappAccountName, accountPaths[additionalDappAccountName])
|
|
48
|
+
additionalDappAccountSigningKeyPayloads.push(signingKeyPayload)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// filter so we only return valid payloads
|
|
53
|
+
return [...additionalDappAccountSigningKeyPayloads, defaultDappAccountSigningKeyPayloads].filter(isAnyPayload) as Payload[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private async resolveXyoWallet() {
|
|
57
|
+
// get the user's wallet
|
|
58
|
+
const settingsCaller = new OsSettingsCaller(this.context)
|
|
59
|
+
const latestWallet = await settingsCaller.getLatestUserWallet()
|
|
60
|
+
|
|
61
|
+
// find the phrase
|
|
62
|
+
const phrase = mnemonicArrayToString(latestWallet.mnemonic.mnemonic)
|
|
63
|
+
const assertedPhrase = assertEx(phrase, () => 'No phrase found in latest wallet')
|
|
64
|
+
|
|
65
|
+
// create a wallet instance
|
|
66
|
+
return await HDWallet.fromPhrase(assertedPhrase)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async signingKeyPayload(wallet: WalletInstance, name: string, path?: string) {
|
|
70
|
+
if (path) {
|
|
71
|
+
// derive the sub account
|
|
72
|
+
const subAccount = await wallet.derivePath(path)
|
|
73
|
+
|
|
74
|
+
// get the private key
|
|
75
|
+
const privateKey = hexFrom(subAccount.privateKey, { prefix: false })
|
|
76
|
+
|
|
77
|
+
// return the payload
|
|
78
|
+
return {
|
|
79
|
+
dappName: name,
|
|
80
|
+
privateKey,
|
|
81
|
+
schema: SigningKeySchema,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DefaultPayloads.ts'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AttachableModuleInstance, ModuleIdentifier } from '@xyo-network/module-model'
|
|
2
|
+
import type { NodeInstance } from '@xyo-network/node-model'
|
|
3
|
+
import type { DappName, ExternalModule, XyOsContext } from '@xyo-network/os-model'
|
|
4
|
+
|
|
5
|
+
// Dapps can only request specific modules from the parent node
|
|
6
|
+
const ALLOWED_MODULES_FROM_PARENT = ['IntentArchivist', 'OsSettingsNode', 'OsPubSubNetworkStackNode', 'OsXyoPublicNetworkStackNode'] as const
|
|
7
|
+
|
|
8
|
+
export class ExternalModulePermissions {
|
|
9
|
+
constructor(
|
|
10
|
+
private context: XyOsContext,
|
|
11
|
+
private dappWindowNode: NodeInstance,
|
|
12
|
+
private dappName: DappName,
|
|
13
|
+
private externalPermissions?: ExternalModule[],
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
async permit() {
|
|
17
|
+
for (const permission of this.externalPermissions ?? []) {
|
|
18
|
+
await this.permitExternalModule(permission)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private getExternalModuleName(permission: ExternalModule) {
|
|
23
|
+
return permission.name as (typeof ALLOWED_MODULES_FROM_PARENT)[number]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private async permitExternalModule(externalPermission: ExternalModule) {
|
|
27
|
+
const externalModuleName = this.getExternalModuleName(externalPermission)
|
|
28
|
+
|
|
29
|
+
if (ALLOWED_MODULES_FROM_PARENT.includes(externalModuleName)) {
|
|
30
|
+
const mod = await this.resolveExternalModule(externalModuleName)
|
|
31
|
+
await this.registerAndAttachModule(mod)
|
|
32
|
+
} else {
|
|
33
|
+
console.warn(`${this.dappName} requested ${externalModuleName} but it was not fulfilled`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private async registerAndAttachModule(mod?: AttachableModuleInstance) {
|
|
38
|
+
if (mod) {
|
|
39
|
+
await this.dappWindowNode.register?.(mod)
|
|
40
|
+
await this.dappWindowNode.attach(mod.address, true)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async resolveExternalModule(id: ModuleIdentifier): Promise<AttachableModuleInstance | undefined> {
|
|
45
|
+
return await this.context.root.resolve(id)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ExternalModulePermissions.ts'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HDWallet } from '@xyo-network/account'
|
|
2
|
+
import { GenericPayloadDiviner } from '@xyo-network/diviner-payload-generic'
|
|
3
|
+
import { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
4
|
+
import type { RegisteredDapp, WindowDappNodeSet, XyOsContext } from '@xyo-network/os-model'
|
|
5
|
+
|
|
6
|
+
import { DappSeedPhraseRepository } from '../../wallet/index.ts'
|
|
7
|
+
import type { DappNodesCreatorParams } from '../lib/index.ts'
|
|
8
|
+
import { DappContextCreator } from './Creator.ts'
|
|
9
|
+
|
|
10
|
+
const GenericPayloadDivinerTags = { 'network.xyo.generic.payload.diviner': 'GenericPayloadDiviner' }
|
|
11
|
+
|
|
12
|
+
const resolveLocator = (existingLocator?: ModuleFactoryLocator) => {
|
|
13
|
+
const locator = existingLocator ?? new ModuleFactoryLocator()
|
|
14
|
+
locator.register(GenericPayloadDiviner, GenericPayloadDivinerTags)
|
|
15
|
+
return locator
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const createDappContext = async (dapp: RegisteredDapp, context: XyOsContext, allowedNames: string[], xnsNodeUrl: string | undefined, xnsNetwork: string | undefined): Promise<WindowDappNodeSet> => {
|
|
19
|
+
console.log('createNodes')
|
|
20
|
+
try {
|
|
21
|
+
const dappSeedPhraseRepository = new DappSeedPhraseRepository(context, allowedNames)
|
|
22
|
+
const seedPhrase = await dappSeedPhraseRepository.request(dapp.config.walletId)
|
|
23
|
+
if (!seedPhrase) {
|
|
24
|
+
throw new Error(`unable to find seed phrase for walletId: ${JSON.stringify(dapp.config, null, 2)}`)
|
|
25
|
+
}
|
|
26
|
+
const wallet = await HDWallet.fromPhrase(seedPhrase)
|
|
27
|
+
const dappName = dapp.config.name
|
|
28
|
+
const { config: dappConfig, params: dappParams } = dapp
|
|
29
|
+
|
|
30
|
+
const locator = resolveLocator((dappParams.locator ?? new ModuleFactoryLocator()).merge(context.platformLocator))
|
|
31
|
+
|
|
32
|
+
const params: DappNodesCreatorParams = {
|
|
33
|
+
config: { dappName, payload: dappConfig.manifest },
|
|
34
|
+
context,
|
|
35
|
+
locator,
|
|
36
|
+
wallet,
|
|
37
|
+
}
|
|
38
|
+
const dappContextCreator = await DappContextCreator.create(params, xnsNodeUrl, xnsNetwork)
|
|
39
|
+
|
|
40
|
+
const dappContext = await dappContextCreator.loadDappContext(context)
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
context: dappContext,
|
|
44
|
+
dappWallet: wallet,
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {
|
|
47
|
+
const error = e as Error
|
|
48
|
+
console.error(`Error creating dappWindow node: ${error.message}`)
|
|
49
|
+
console.error(`Error creating dappWindow node: ${error.stack}`)
|
|
50
|
+
throw new Error(`Error creating dappWindow node: ${error.message}`)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { forget } from '@xylabs/forget'
|
|
2
|
+
import type { Promisable } from '@xylabs/promise'
|
|
3
|
+
import { fulfilled, rejected } from '@xylabs/promise'
|
|
4
|
+
import { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
5
|
+
import type {
|
|
6
|
+
DappPackageManifestPayload,
|
|
7
|
+
DappParams,
|
|
8
|
+
DappRegistrationResults,
|
|
9
|
+
PayloadWithVersion,
|
|
10
|
+
RegisteredDappSetResult,
|
|
11
|
+
XyOsContext,
|
|
12
|
+
XyOsDappContext } from '@xyo-network/os-model'
|
|
13
|
+
import {
|
|
14
|
+
DappIntentTypes,
|
|
15
|
+
DappMode,
|
|
16
|
+
isRegisteredDappAccessDappSet,
|
|
17
|
+
isRegisteredDappExposedDappSet,
|
|
18
|
+
} from '@xyo-network/os-model'
|
|
19
|
+
|
|
20
|
+
import { dappAccessRequestConnection, DappAccessRequestEvent, exposeDappRequestConnection, ExposeDappRequestEvent } from '../../event/index.ts'
|
|
21
|
+
import { DappIntentCaller } from '../../intent/index.ts'
|
|
22
|
+
import { NameTransforms } from '../../lib/index.ts'
|
|
23
|
+
import { DappSeedPhraseRepository } from '../../wallet/index.ts'
|
|
24
|
+
import { XyOsDapp } from '../../XyOsDapp.ts'
|
|
25
|
+
import { ManageSystemDapps } from '../system/index.ts'
|
|
26
|
+
import { DappRegistry } from './DappRegistry.ts'
|
|
27
|
+
|
|
28
|
+
export interface DappRegistrationServiceParams {
|
|
29
|
+
dappNames: string[]
|
|
30
|
+
dappParams: Record<string, DappParams>
|
|
31
|
+
dappPayloads: PayloadWithVersion[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class DappRegistrationService {
|
|
35
|
+
// Dapps that have been built with their own context
|
|
36
|
+
builtDapps: Record<string, XyOsDappContext> = {}
|
|
37
|
+
|
|
38
|
+
dappRegistry: DappRegistry
|
|
39
|
+
dappSeedPhraseRepository: DappSeedPhraseRepository
|
|
40
|
+
manageSystemDapps: ManageSystemDapps
|
|
41
|
+
|
|
42
|
+
// Dapps that have been registered with dappAccessRequests
|
|
43
|
+
registeredAccessDappSets: Set<RegisteredDappSetResult> = new Set()
|
|
44
|
+
|
|
45
|
+
// Dapps that have been registered with the dapp registry
|
|
46
|
+
registeredDappSets: Set<RegisteredDappSetResult> = new Set()
|
|
47
|
+
|
|
48
|
+
// Dapps that have been registered with exposeDappRequests
|
|
49
|
+
registeredExposedDappSets: Set<RegisteredDappSetResult> = new Set()
|
|
50
|
+
|
|
51
|
+
private dappAccessRequestConnection = dappAccessRequestConnection()
|
|
52
|
+
private exposeDappRequestConnection = exposeDappRequestConnection()
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
private context: XyOsContext,
|
|
56
|
+
private params: DappRegistrationServiceParams = { dappNames: [], dappParams: {}, dappPayloads: [] },
|
|
57
|
+
private locator = new ModuleFactoryLocator(),
|
|
58
|
+
private developmentMode?: boolean,
|
|
59
|
+
) {
|
|
60
|
+
this.manageSystemDapps = new ManageSystemDapps(
|
|
61
|
+
context,
|
|
62
|
+
this.params?.dappNames ?? [],
|
|
63
|
+
this.params?.dappPayloads ?? [],
|
|
64
|
+
this.params?.dappParams ?? {},
|
|
65
|
+
this.locator,
|
|
66
|
+
this.developmentMode,
|
|
67
|
+
)
|
|
68
|
+
this.dappSeedPhraseRepository = new DappSeedPhraseRepository(context, this.params?.dappNames ?? [])
|
|
69
|
+
this.dappRegistry = new DappRegistry(this.dappSeedPhraseRepository)
|
|
70
|
+
|
|
71
|
+
this.addConnectionRequests()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
buildDapp(
|
|
75
|
+
manifest: DappPackageManifestPayload,
|
|
76
|
+
dappId = NameTransforms.slug(manifest.nodes[0]?.config.name),
|
|
77
|
+
name = NameTransforms.moduleName(manifest.nodes[0]?.config.name),
|
|
78
|
+
): Promisable<XyOsDappContext> {
|
|
79
|
+
const dapp = new XyOsDapp({ dapp: { id: dappId, name }, locator: this.locator, manifest, parent: this.context }) as XyOsDappContext
|
|
80
|
+
this.builtDapps[dappId] = dapp
|
|
81
|
+
return dapp
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getParamsForRegisteredDapp(dappId: string) {
|
|
85
|
+
return this.params?.dappParams[dappId]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async start(): Promise<DappRegistrationResults> {
|
|
89
|
+
const systemDapps = await this.context.monitor(async () => await this.manageSystemDapps.install(), { name: 'Install System dApps' })
|
|
90
|
+
const activeDapps = systemDapps.filter(({ dapp }) => dapp.icon.active === true)
|
|
91
|
+
const results = await Promise.allSettled(
|
|
92
|
+
activeDapps.map(async (dappSet) => {
|
|
93
|
+
const { dapp } = dappSet
|
|
94
|
+
const registeredDapp = await this.dappRegistry.registerDapp(dapp)
|
|
95
|
+
const result: RegisteredDappSetResult = { dapp: registeredDapp, dappIcon: dapp.icon }
|
|
96
|
+
|
|
97
|
+
// Start post registration process in the background
|
|
98
|
+
const forgetHandlers = async () => {
|
|
99
|
+
try {
|
|
100
|
+
await this.postRegistrationHandlers(result)
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error('Error in post registration handlers', e)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
forget(forgetHandlers())
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
}),
|
|
109
|
+
)
|
|
110
|
+
const failed = results.filter(rejected).map(result => result.reason)
|
|
111
|
+
const succeeded = results.filter(fulfilled).map(result => result.value)
|
|
112
|
+
|
|
113
|
+
for (const registeredDapp of succeeded) this.registeredDappSets.add(registeredDapp)
|
|
114
|
+
|
|
115
|
+
return { failed, succeeded }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private addConnectionRequests() {
|
|
119
|
+
this.context.eventBus.addConnection(this.dappAccessRequestConnection)
|
|
120
|
+
this.context.eventBus.addConnection(this.exposeDappRequestConnection)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private async postRegistrationHandlers(registeredDapp: RegisteredDappSetResult) {
|
|
124
|
+
if (registeredDapp.dapp) {
|
|
125
|
+
if (isRegisteredDappExposedDappSet(registeredDapp)) {
|
|
126
|
+
// add to exposed dapp set
|
|
127
|
+
this.registeredExposedDappSets.add(registeredDapp)
|
|
128
|
+
// craft intent
|
|
129
|
+
const exposeIntent = DappIntentCaller.buildIntent(
|
|
130
|
+
DappIntentCaller.OsDappName,
|
|
131
|
+
DappIntentTypes.Launch,
|
|
132
|
+
registeredDapp.dapp.config.name,
|
|
133
|
+
DappMode.Exposed,
|
|
134
|
+
)
|
|
135
|
+
// emit to bus
|
|
136
|
+
await this.exposeDappRequestConnection.emit(ExposeDappRequestEvent, { payloads: [registeredDapp.dapp.config, exposeIntent] })
|
|
137
|
+
}
|
|
138
|
+
if (isRegisteredDappAccessDappSet(registeredDapp)) {
|
|
139
|
+
// add to access dapp set
|
|
140
|
+
this.registeredAccessDappSets.add(registeredDapp)
|
|
141
|
+
// emit to bus
|
|
142
|
+
await this.dappAccessRequestConnection.emit(DappAccessRequestEvent, { payloads: registeredDapp.dapp.accessors })
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Hash } from '@xylabs/hex'
|
|
2
|
+
import { HDWallet } from '@xyo-network/account'
|
|
3
|
+
import type {
|
|
4
|
+
DappPackageManifestPayload,
|
|
5
|
+
DappParams,
|
|
6
|
+
RegisteredDapp,
|
|
7
|
+
RegisteredDappAccess,
|
|
8
|
+
UnregisteredDapp,
|
|
9
|
+
UnregisteredDappAccess } from '@xyo-network/os-model'
|
|
10
|
+
import {
|
|
11
|
+
DappRegisteredSchema,
|
|
12
|
+
DappRegisteredState,
|
|
13
|
+
isRegisteredDappAccess,
|
|
14
|
+
} from '@xyo-network/os-model'
|
|
15
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
16
|
+
|
|
17
|
+
import type { DappSeedPhraseRepository } from '../../wallet/index.ts'
|
|
18
|
+
import type {
|
|
19
|
+
FailedAccessor } from './ValidateDappAccessDiviner/index.ts'
|
|
20
|
+
import {
|
|
21
|
+
isFailedAccessor,
|
|
22
|
+
ValidateDappAccessDiviner,
|
|
23
|
+
ValidateDappAccessDivinerConfigSchema,
|
|
24
|
+
} from './ValidateDappAccessDiviner/index.ts'
|
|
25
|
+
|
|
26
|
+
export type DappRegisteredId = string
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Manage Dapp Registration
|
|
30
|
+
*/
|
|
31
|
+
export class DappRegistry {
|
|
32
|
+
private dappRegistry: Map<DappRegisteredId, RegisteredDapp> = new Map()
|
|
33
|
+
|
|
34
|
+
constructor(private dappSeedPhraseRepository: DappSeedPhraseRepository) {}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a Dapp with window manager and if successful, return its id
|
|
38
|
+
*
|
|
39
|
+
* @param {UnregisteredDapp} dapp Manifest and UI of the Dapp to register
|
|
40
|
+
* @returns {RegisteredDapp} dapp with registration fields
|
|
41
|
+
**/
|
|
42
|
+
async registerDapp(dapp?: UnregisteredDapp): Promise<RegisteredDapp | null> {
|
|
43
|
+
if (dapp) {
|
|
44
|
+
const { exposedModuleIds, manifest, modes, name, version, params, widgetConfigs } = this.extractDappProperties(dapp)
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
/**
|
|
48
|
+
* TODO: More validation from sources is needed for third-party dapps. A name needs some form of verification
|
|
49
|
+
* before trusting it with a seed phrase at that given name.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
// get a seed phrase for the given dapp id
|
|
53
|
+
const walletId = await this.dappSeedPhraseRepository.findOrCreate(name)
|
|
54
|
+
|
|
55
|
+
const registeredAccessors = await this.validateDappAccessPayloads(manifest, dapp.accessors, params)
|
|
56
|
+
|
|
57
|
+
const registeredDapp: RegisteredDapp = {
|
|
58
|
+
accessors: registeredAccessors,
|
|
59
|
+
config: {
|
|
60
|
+
exposedModuleIds,
|
|
61
|
+
manifest,
|
|
62
|
+
modes,
|
|
63
|
+
name,
|
|
64
|
+
schema: DappRegisteredSchema,
|
|
65
|
+
state: DappRegisteredState.Registered,
|
|
66
|
+
version,
|
|
67
|
+
// attach the walletId to a registered dapp so it can request it back in the future
|
|
68
|
+
walletId,
|
|
69
|
+
},
|
|
70
|
+
params,
|
|
71
|
+
widgetConfigs,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.dappRegistry.get(name)) {
|
|
75
|
+
console.warn('tried to register two dapps with the same name', name)
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
this.dappRegistry.set(name, registeredDapp)
|
|
79
|
+
return registeredDapp
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error('Error registeringDapp', e)
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Unregister a dapp so it can no longer be launched
|
|
91
|
+
*
|
|
92
|
+
* @param dappId
|
|
93
|
+
*/
|
|
94
|
+
unregisterDapp(dappId: Hash) {
|
|
95
|
+
this.dappRegistry.delete(dappId)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private extractDappProperties(dapp: UnregisteredDapp) {
|
|
99
|
+
const { params, config, widgetConfigs } = dapp
|
|
100
|
+
const { exposedModuleIds, manifest, modes, name, sources, version } = config
|
|
101
|
+
|
|
102
|
+
return { exposedModuleIds, manifest, modes, name, params, sources, version, widgetConfigs }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async validateDappAccessPayloads(manifest: DappPackageManifestPayload, accessors: UnregisteredDappAccess[] = [], params: DappParams) {
|
|
106
|
+
const validateDappAccess = await ValidateDappAccessDiviner.create({
|
|
107
|
+
account: await HDWallet.random(),
|
|
108
|
+
config: { schema: ValidateDappAccessDivinerConfigSchema },
|
|
109
|
+
dappParams: params,
|
|
110
|
+
})
|
|
111
|
+
const payloads = await validateDappAccess.divine([manifest, ...accessors])
|
|
112
|
+
const failedAccessors = payloads.filter(isFailedAccessor) as WithMeta<FailedAccessor>[]
|
|
113
|
+
const registeredAccessors = payloads.filter(isRegisteredDappAccess) as WithMeta<RegisteredDappAccess>[]
|
|
114
|
+
|
|
115
|
+
if (failedAccessors.length > 0) {
|
|
116
|
+
console.error('Failed to register all dapp accessors', failedAccessors)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return registeredAccessors
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DivinerParams } from '@xyo-network/diviner-model'
|
|
2
|
+
import type { PackageManifestPayload } from '@xyo-network/manifest'
|
|
3
|
+
import type { DappPackageManifestPayload, DappParams, RegisteredDappAccess, UnregisteredDappAccess } from '@xyo-network/os-model'
|
|
4
|
+
import type { Payload, WithMeta } from '@xyo-network/payload-model'
|
|
5
|
+
import { isPayloadOfSchemaTypeWithMeta } from '@xyo-network/payload-model'
|
|
6
|
+
|
|
7
|
+
export const FailedAccessorSchema = 'network.xyo.os.failed.accessor.registration' as const
|
|
8
|
+
export type FailedAccessorSchema = typeof FailedAccessorSchema
|
|
9
|
+
|
|
10
|
+
export type FailedAccessor = Payload<{ accessor: UnregisteredDappAccess; errorMessage: string }, FailedAccessorSchema>
|
|
11
|
+
|
|
12
|
+
export const isFailedAccessor = isPayloadOfSchemaTypeWithMeta<FailedAccessor>(FailedAccessorSchema)
|
|
13
|
+
|
|
14
|
+
export type ValidateDappAccessDivinerParams = DivinerParams & { dappParams: DappParams }
|
|
15
|
+
export type ValidateDappAccessDivinerIn = DappPackageManifestPayload | PackageManifestPayload | UnregisteredDappAccess
|
|
16
|
+
export type ValidateDappAccessDivinerOut = WithMeta<RegisteredDappAccess> | WithMeta<FailedAccessor>
|
|
17
|
+
|
|
18
|
+
export const ValidateDappAccessDivinerConfigSchema = 'network.xyo.os.dapp.access.registration.validate' as const
|
|
19
|
+
export type ValidateDappAccessDivinerConfigSchema = typeof ValidateDappAccessDivinerConfigSchema
|