-
Notifications
You must be signed in to change notification settings - Fork 98
feat: Add custom domains and split-face architecture to the bootstrap server #764
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "trailingComma": "all", | ||
| "tabWidth": 2, | ||
| "printWidth": 100, | ||
| "semi": true, | ||
| "bracketSpacing": true, | ||
| "useTabs": false, | ||
| "singleQuote": true, | ||
| "arrowParens": "avoid" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,27 @@ | ||
| import { blobFromUint8Array } from '@dfinity/agent'; | ||
| import {httpAgent, canisterIdFactory} from '../agent'; | ||
| import { httpAgent, canisterIdFactory } from '../agent'; | ||
| import * as path from 'path'; | ||
| import { readFileSync } from 'fs'; | ||
| import { default as idl, Identity } from "./identity/main.did"; | ||
| import { default as idl, Identity } from './identity/main.did'; | ||
|
|
||
| const wasm = readFileSync(path.join(__dirname, 'identity/main.wasm')); | ||
| const factory = httpAgent.makeActorFactory(idl); | ||
|
|
||
| // TODO(hansl): Add a type to create an Actor interface from a IDL.Service definition. | ||
| export async function identityFactory(): Promise<Identity> { | ||
| let actor = await factory({ httpAgent }) as Identity; | ||
| let actor = (await factory({ agent: httpAgent })) as Identity; | ||
| let cid = await actor.__createCanister(); | ||
| actor.__setCanisterId(cid); | ||
|
|
||
| await actor.__install({ | ||
| module: blobFromUint8Array(wasm), | ||
| }, { | ||
| maxAttempts: 600, | ||
| throttleDurationInMSecs: 100, | ||
| }); | ||
| await actor.__install( | ||
| { | ||
| module: blobFromUint8Array(wasm), | ||
| }, | ||
| { | ||
| maxAttempts: 600, | ||
| throttleDurationInMSecs: 100, | ||
| }, | ||
| ); | ||
|
|
||
| return actor; | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { Buffer } from 'buffer/'; | ||
| import { Agent } from './agent'; | ||
| import { CanisterId } from './canisterId'; | ||
| import { HttpAgent } from './http_agent'; | ||
| import { | ||
| QueryResponseStatus, | ||
| RequestStatusResponse, | ||
|
|
@@ -9,6 +9,7 @@ import { | |
| SubmitResponse, | ||
| } from './http_agent_types'; | ||
| import * as IDL from './idl'; | ||
| import { GlobalInternetComputer } from './index'; | ||
| import { RequestId, toHex as requestIdToHex } from './request_id'; | ||
| import { BinaryBlob } from './types'; | ||
|
|
||
|
|
@@ -39,23 +40,30 @@ export type Actor = Record<string, (...args: unknown[]) => Promise<unknown>> & { | |
|
|
||
| export interface ActorConfig { | ||
| canisterId?: string | CanisterId; | ||
| httpAgent?: HttpAgent; | ||
| agent?: Agent; | ||
| maxAttempts?: number; | ||
| throttleDurationInMSecs?: number; | ||
| } | ||
|
|
||
| declare const window: { icHttpAgent?: HttpAgent }; | ||
| declare const global: { icHttpAgent?: HttpAgent }; | ||
| declare const self: { icHttpAgent?: HttpAgent }; | ||
|
|
||
| function getDefaultHttpAgent() { | ||
| return typeof window === 'undefined' | ||
| ? typeof global === 'undefined' | ||
| ? typeof self === 'undefined' | ||
| ? undefined | ||
| : self.icHttpAgent | ||
| : global.icHttpAgent | ||
| : window.icHttpAgent; | ||
| declare const window: GlobalInternetComputer; | ||
| declare const global: GlobalInternetComputer; | ||
| declare const self: GlobalInternetComputer; | ||
|
|
||
| function getDefaultAgent(): Agent { | ||
| const agent = | ||
| typeof window === 'undefined' | ||
| ? typeof global === 'undefined' | ||
| ? typeof self === 'undefined' | ||
| ? undefined | ||
| : self.ic.agent | ||
| : global.ic.agent | ||
| : window.ic.agent; | ||
|
|
||
| if (!agent) { | ||
| throw new Error('No Agent could be found.'); | ||
| } | ||
|
|
||
| return agent; | ||
| } | ||
|
|
||
| // IDL functions can have multiple return values, so decoding always | ||
|
|
@@ -86,31 +94,31 @@ export type ActorConstructor = (config: ActorConfig) => Actor; | |
| // Allows for one HTTP agent for the lifetime of the actor: | ||
| // | ||
| // ``` | ||
| // const actor = makeActor(actorInterface)(httpAgent); | ||
| // const actor = makeActor(actorInterface)({ agent }); | ||
| // const reply = await actor.greet(); | ||
| // ``` | ||
| // | ||
| // or using a different HTTP agent for the same actor if necessary: | ||
| // | ||
| // ``` | ||
| // const actor = makeActor(actorInterface); | ||
| // const reply1 = await actor(httpAgent1).greet(); | ||
| // const reply2 = await actor(httpAgent2).greet(); | ||
| // const reply1 = await actor(agent1).greet(); | ||
| // const reply2 = await actor(agent2).greet(); | ||
| // ``` | ||
| export function makeActorFactory( | ||
| actorInterfaceFactory: (_: { IDL: typeof IDL }) => IDL.ServiceClass, | ||
| ): ActorConstructor { | ||
| const actorInterface = actorInterfaceFactory({ IDL }); | ||
|
|
||
| async function requestStatusAndLoop<T>( | ||
| httpAgent: HttpAgent, | ||
| agent: Agent, | ||
| requestId: RequestId, | ||
| decoder: (response: RequestStatusResponseReplied) => T, | ||
| attempts: number, | ||
| maxAttempts: number, | ||
| throttle: number, | ||
| ): Promise<T> { | ||
| const status = await httpAgent.requestStatus({ requestId }); | ||
| const status = await agent.requestStatus({ requestId }); | ||
|
|
||
| switch (status.status) { | ||
| case RequestStatusResponseStatus.Replied: { | ||
|
|
@@ -130,7 +138,7 @@ export function makeActorFactory( | |
|
|
||
| // Wait a little, then retry. | ||
| return new Promise(resolve => setTimeout(resolve, throttle)).then(() => | ||
| requestStatusAndLoop(httpAgent, requestId, decoder, attempts, maxAttempts, throttle), | ||
| requestStatusAndLoop(agent, requestId, decoder, attempts, maxAttempts, throttle), | ||
| ); | ||
|
|
||
| case RequestStatusResponseStatus.Rejected: | ||
|
|
@@ -144,7 +152,7 @@ export function makeActorFactory( | |
| } | ||
|
|
||
| return (config: ActorConfig) => { | ||
| const { canisterId, maxAttempts, throttleDurationInMSecs, httpAgent } = { | ||
| const { canisterId, maxAttempts, throttleDurationInMSecs, agent: configAgent } = { | ||
| ...DEFAULT_ACTOR_CONFIG, | ||
| ...config, | ||
| } as Required<ActorConfig>; | ||
|
|
@@ -166,15 +174,26 @@ export function makeActorFactory( | |
| return cid?.toHex(); | ||
| }, | ||
| async __getAsset(path: string) { | ||
| const agent = httpAgent || getDefaultHttpAgent(); | ||
| if (!agent) { | ||
| throw new Error('Cannot make call. httpAgent is undefined.'); | ||
| } | ||
| const agent = configAgent || getDefaultAgent(); | ||
| if (!cid) { | ||
| throw new Error('Cannot make call. Canister ID is undefined.'); | ||
| } | ||
|
|
||
| return agent.retrieveAsset(cid, path); | ||
| const arg = IDL.encode([IDL.Text], [path]) as BinaryBlob; | ||
| return agent.query(canisterId, { methodName: 'retrieve', arg }).then(response => { | ||
| switch (response.status) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want to have
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The object is built from the public spec, so there is only two |
||
| case QueryResponseStatus.Rejected: | ||
| throw new Error( | ||
| `An error happened while retrieving asset "${path}":\n` + | ||
| ` Status: ${response.status}\n` + | ||
| ` Message: ${response.reject_message}\n`, | ||
| ); | ||
|
|
||
| case QueryResponseStatus.Replied: | ||
| const [content] = IDL.decode([IDL.Vec(IDL.Nat8)], response.reply.arg); | ||
| return new Uint8Array(content as number[]); | ||
| } | ||
| }); | ||
| }, | ||
| __setCanisterId(newCid: CanisterId): void { | ||
| cid = newCid; | ||
|
|
@@ -185,11 +204,7 @@ export function makeActorFactory( | |
| throttleDurationInMSecs?: number; | ||
| } = {}, | ||
| ): Promise<CanisterId> { | ||
| const agent = httpAgent || getDefaultHttpAgent(); | ||
| if (!agent) { | ||
| throw new Error('Cannot make call. httpAgent is undefined.'); | ||
| } | ||
|
|
||
| const agent = configAgent || getDefaultAgent(); | ||
| // Resolve the options that can be used globally or locally. | ||
| const effectiveMaxAttempts = options.maxAttempts?.valueOf() || 0; | ||
| const effectiveThrottle = options.throttleDurationInMSecs?.valueOf() || 0; | ||
|
|
@@ -233,10 +248,7 @@ export function makeActorFactory( | |
| throttleDurationInMSecs?: number; | ||
| } = {}, | ||
| ) { | ||
| const agent = httpAgent || getDefaultHttpAgent(); | ||
| if (!agent) { | ||
| throw new Error('Cannot make call. httpAgent is undefined.'); | ||
| } | ||
| const agent = configAgent || getDefaultAgent(); | ||
| if (!cid) { | ||
| throw new Error('Cannot make call. Canister ID is undefined.'); | ||
| } | ||
|
|
@@ -271,10 +283,7 @@ export function makeActorFactory( | |
|
|
||
| for (const [methodName, func] of actorInterface._fields) { | ||
| actor[methodName] = async (...args: any[]) => { | ||
| const agent = httpAgent || getDefaultHttpAgent(); | ||
| if (!agent) { | ||
| throw new Error('Cannot make call. httpAgent is undefined.'); | ||
| } | ||
| const agent = configAgent || getDefaultAgent(); | ||
| if (!cid) { | ||
| throw new Error('Cannot make call. Canister ID is undefined.'); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { ActorConstructor } from '../actor'; | ||
| import { CanisterId } from '../canisterId'; | ||
| import { | ||
| QueryFields, | ||
| QueryResponse, | ||
| RequestStatusFields, | ||
| RequestStatusResponse, | ||
| SubmitResponse, | ||
| } from '../http_agent_types'; | ||
| import * as IDL from '../idl'; | ||
| import { Principal } from '../principal'; | ||
| import { BinaryBlob } from '../types'; | ||
|
|
||
| // An Agent able to make calls and queries to a Replica. | ||
| export interface Agent { | ||
| requestStatus(fields: RequestStatusFields, principal?: Principal): Promise<RequestStatusResponse>; | ||
|
|
||
| call( | ||
| canisterId: CanisterId | string, | ||
| fields: { | ||
| methodName: string; | ||
| arg: BinaryBlob; | ||
| }, | ||
| principal?: Principal | Promise<Principal>, | ||
| ): Promise<SubmitResponse>; | ||
|
|
||
| createCanister(principal?: Principal): Promise<SubmitResponse>; | ||
|
|
||
| install( | ||
| canisterId: CanisterId | string, | ||
| fields: { | ||
| module: BinaryBlob; | ||
| arg?: BinaryBlob; | ||
| }, | ||
| principal?: Principal, | ||
| ): Promise<SubmitResponse>; | ||
|
|
||
| query( | ||
| canisterId: CanisterId | string, | ||
| fields: QueryFields, | ||
| principal?: Principal, | ||
| ): Promise<QueryResponse>; | ||
|
|
||
| makeActorFactory( | ||
| actorInterfaceFactory: (_: { IDL: typeof IDL }) => IDL.ServiceClass, | ||
| ): ActorConstructor; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we're moving to "agent" instead of httpAgent? I don't totally follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
httpAgentis the Agent implementation that talks over HTTP. In this PR we introducedProxyAgentthat talks to a proxy function, and we could have more agents (MockAgent, SmithAgent, etc)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh okay, nice