Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

This API is a Node.js server that will be running on a Linux VM.

It is composed of one endpoint:

- `/sconify`:

- Takes a public dockerhub image as input and a user auth token with push
access to the image's repo in order to push the sconified image,
- builds a sconified image out of it,
- publishes it to dockerhub with tag suffix,
- deploys an app contract on Bellecour.

- `/` or any other endpoint: will return a simple text (mostly to check if the
server is running)
The API is composed of:

- HTTP endpoints:

- 🟢 `GET /`: will return a simple text (mostly to check if the server is
running)
- 🟢 `GET /health`: will return a JSON object with the health status of the
server and the version of the API.
- 🟠 `POST /sconify`: deprecated, use Websocket API request `SCONIFY_BUILD`
instead.
- 🟠 `POST /sconify/build`: deprecated, use Websocket API request
`SCONIFY_BUILD` instead.

- Websocket API requests:
- 🟢 `SCONIFY_BUILD`: will take a dockerhub image and return a
`sconifiedImage` and `appContractAddress` in the response.
- Takes a public dockerhub image as input and a user auth token with push
access to the image's repo in order to push the sconified image
- builds a sconified image out of it
- publishes it to dockerhub with tag suffix
- 🟠 `SCONIFY`: deprecated, use `SCONIFY_BUILD` instead.

## Prerequisites

Expand Down Expand Up @@ -41,3 +50,14 @@ npm run start
```sh
npm run dev:pretty
```

## deprecations

- `POST /sconify` is deprecated, websocket API request `SCONIFY_BUILD` should be
used instead.
- `POST /sconify/build` is deprecated, websocket API request `SCONIFY_BUILD`
should be used instead.
- websocket API request `SCONIFY` is deprecated, websocket API request
`SCONIFY_BUILD` should be used instead.
- template `Python` is deprecated, template `Python3.13` should be used instead.
- sconeVersion `v5` is deprecated, sconeVersion `v5.9` should be used instead.
4 changes: 2 additions & 2 deletions api/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ export const TEMPLATE_CONFIG: Record<
JavaScript: {
// node binary name does not change from one version to another
binary: '/usr/local/bin/node',
// for scone 5.7 this was necessary
// deprecated for scone 5.7 this was necessary
sconeCuratedImage:
'registry.scontain.com:5050/sconecuratedimages/node:14.4.0-alpine3.11',
},
'Python3.13': {
binary: '/usr/local/bin/python3.13',
},
// legacy template name Python used Python 3.8
// deprecated legacy template name Python used Python 3.8
Python: {
binary: '/usr/local/bin/python3.8',
},
Expand Down
4 changes: 2 additions & 2 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { requestIdMiddleware } from './utils/requestId.js';
import { errorHandlerMiddleware } from './utils/errors.js';
import { attachWebSocketServer } from './utils/websocket.js';
import {
sconifyBuildHttpHandler,
deprecated_sconifyBuildHttpHandler,
sconifyBuildWsHandler,
} from './sconify/sconifyBuild.handler.js';

Expand All @@ -34,7 +34,7 @@ app.use(loggerMiddleware);
// deprecated endpoint, clients should use /sconify/build
app.post('/sconify', deprecated_sconifyHttpHandler);

app.post('/sconify/build', sconifyBuildHttpHandler);
app.post('/sconify/build', deprecated_sconifyBuildHttpHandler);

// Health endpoint
app.get('/health', (req, res) => {
Expand Down
12 changes: 7 additions & 5 deletions api/src/sconify/deprecated_sconify.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TEMPLATE_CONFIG, type TemplateName } from '../constants/constants.js';
import { ethereumAddressZodSchema } from '../utils/ethereumAddressZodSchema.js';
import { deprecated_sconify } from './deprecated_sconify.service.js';
import type { Request, Response } from 'express';
import { logger } from '../utils/logger.js';

const bodySchema = z.object({
yourWalletPublicAddress: ethereumAddressZodSchema,
Expand All @@ -25,7 +26,7 @@ const bodySchema = z.object({
.default('JavaScript'),
});

async function handleSconifyRequest(requestObj: object) {
async function deprecated_handleSconifyRequest(requestObj: object) {
let yourWalletPublicAddress;
let dockerhubImageToSconify;
let dockerhubPushToken;
Expand Down Expand Up @@ -54,18 +55,19 @@ async function handleSconifyRequest(requestObj: object) {
}

export async function deprecated_sconifyWsHandler(message: object) {
logger.warn('deprecated feature hit: ws request SCONIFY');
const { sconifiedImage, appContractAddress } =
await handleSconifyRequest(message);
await deprecated_handleSconifyRequest(message);
return { sconifiedImage, appContractAddress };
}

export async function deprecated_sconifyHttpHandler(
req: Request,
res: Response
) {
const { sconifiedImage, appContractAddress } = await handleSconifyRequest(
req.body || {}
);
logger.warn('deprecated feature hit: POST /sconify');
const { sconifiedImage, appContractAddress } =
await deprecated_handleSconifyRequest(req.body || {});
res.status(200).json({
success: true,
sconifiedImage,
Expand Down
14 changes: 13 additions & 1 deletion api/src/sconify/sconifyBuild.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { ethereumAddressZodSchema } from '../utils/ethereumAddressZodSchema.js';
import { sconify } from './sconifyBuild.service.js';
import type { Request, Response } from 'express';
import { logger } from '../utils/logger.js';

const bodySchema = z.object({
yourWalletPublicAddress: ethereumAddressZodSchema,
Expand Down Expand Up @@ -51,6 +52,13 @@ async function handleSconifyRequest(requestObj: object) {
}),
});
}
if (template === 'Python') {
logger.warn('Deprecated feature hit: template === "Python"');
}
if (sconeVersion === 'v5') {
logger.warn('Deprecated feature hit: sconeVersion === "v5"');
}

const { dockerImage, dockerImageDigest, fingerprint, entrypoint } =
await sconify({
dockerImageToSconify: dockerhubImageToSconify,
Expand Down Expand Up @@ -85,7 +93,11 @@ export async function sconifyBuildWsHandler(message: object) {
};
}

export async function sconifyBuildHttpHandler(req: Request, res: Response) {
export async function deprecated_sconifyBuildHttpHandler(
req: Request,
res: Response
) {
logger.warn('Deprecated feature hit: POST /sconify/build');
const {
dockerImage,
dockerImageDigest,
Expand Down
1 change: 0 additions & 1 deletion cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useExperimentalNetworks } from '../utils/featureFlags.js';
export const SCONE_TAG = ['tee', 'scone'];
export const DEFAULT_SCONE_VERSION = 'v5.9';

export const SCONIFY_API_HTTP_URL = 'https://iapp-api.iex.ec';
export const SCONIFY_API_WS_URL = 'wss://iapp-api.iex.ec';

export const CONFIG_FILE = 'iapp.config.json';
Expand Down
1 change: 0 additions & 1 deletion cli/src/utils/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import chalk from 'chalk';

export const useWsApi = checkFlag('EXPERIMENTAL_WS_API');
export const useTdx = checkFlag('EXPERIMENTAL_TDX_APP');
export const useExperimentalNetworks = checkFlag('EXPERIMENTAL_NETWORKS');

Expand Down
186 changes: 67 additions & 119 deletions cli/src/utils/sconify.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
DEFAULT_SCONE_VERSION,
SCONIFY_API_HTTP_URL,
SCONIFY_API_WS_URL,
} from '../config/config.js';
import { DEFAULT_SCONE_VERSION, SCONIFY_API_WS_URL } from '../config/config.js';
import { getAuthToken } from './dockerhub.js';
import { sleep } from './sleep.js';
import {
Expand All @@ -11,7 +7,6 @@ import {
serializeData,
} from './websocket.js';
import { debug } from './debug.js';
import { useWsApi } from './featureFlags.js';

const INITIAL_RETRY_PERIOD = 20 * 1000; // 20s

Expand Down Expand Up @@ -56,133 +51,86 @@ export async function sconify({

const pushToken = await getPushToken();

let sconifyResult: {
const sconifyResult: {
dockerImage?: string;
dockerImageDigest?: string;
entrypoint?: string;
fingerprint?: string;
sconeVersion?: string;
};

if (useWsApi) {
// experimental ws connection
sconifyResult = await new Promise((resolve, reject) => {
createReconnectingWs(SCONIFY_API_WS_URL, {
headers: {
'x-wallet': walletAddress,
},
connectCallback: (ws) => {
const handleError = (e: unknown) => {
ws.close(1000); // normal ws close
reject(e);
};
} = await new Promise((resolve, reject) => {
createReconnectingWs(SCONIFY_API_WS_URL, {
headers: {
'x-wallet': walletAddress,
},
connectCallback: (ws) => {
const handleError = (e: unknown) => {
ws.close(1000); // normal ws close
reject(e);
};

ws.on('message', (data) => {
let message;
// handle communication errors
try {
message = deserializeData(data);
debug(`ws message: ${JSON.stringify(message, undefined, 2)}`);
} catch (e) {
handleError(e);
}
ws.on('message', (data) => {
let message;
// handle communication errors
try {
message = deserializeData(data);
debug(`ws message: ${JSON.stringify(message, undefined, 2)}`);
} catch (e) {
handleError(e);
}

// handle server responses
if (message?.type === 'RESPONSE') {
if (message?.target === 'SCONIFY_BUILD') {
ws.close(1000); // normal ws close
if (message?.success === true && message.result) {
resolve(message.result);
} else {
reject(Error(message.error || 'Server unknown error'));
}
// handle server responses
if (message?.type === 'RESPONSE') {
if (message?.target === 'SCONIFY_BUILD') {
ws.close(1000); // normal ws close
if (message?.success === true && message.result) {
resolve(message.result);
} else {
reject(Error(message.error || 'Server unknown error'));
}
}
}

// handle server requests
if (message?.type === 'REQUEST') {
if (message?.target === 'RENEW_PUSH_TOKEN') {
getPushToken()
.then((renewedPushToken) => {
ws.send(
serializeData({
type: 'RESPONSE',
target: 'RENEW_PUSH_TOKEN',
result: {
dockerhubPushToken: renewedPushToken,
},
})
);
})
.catch(handleError);
}
// handle server requests
if (message?.type === 'REQUEST') {
if (message?.target === 'RENEW_PUSH_TOKEN') {
getPushToken()
.then((renewedPushToken) => {
ws.send(
serializeData({
type: 'RESPONSE',
target: 'RENEW_PUSH_TOKEN',
result: {
dockerhubPushToken: renewedPushToken,
},
})
);
})
.catch(handleError);
}
}

// handle server info
if (message?.type === 'INFO') {
// TODO server feedback
}
});
},
initCallback: (ws) => {
ws.send(
serializeData({
type: 'REQUEST',
target: 'SCONIFY_BUILD', // call sconify handler
template,
dockerhubImageToSconify: iAppNameToSconify,
dockerhubPushToken: pushToken,
yourWalletPublicAddress: walletAddress,
sconeVersion: DEFAULT_SCONE_VERSION,
})
);
},
errorCallback: reject,
});
});
} else {
// standard http call
sconifyResult = await fetch(`${SCONIFY_API_HTTP_URL}/sconify/build`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-wallet': walletAddress,
// handle server info
if (message?.type === 'INFO') {
// TODO server feedback
}
});
},
body: JSON.stringify({
template,
dockerhubImageToSconify: iAppNameToSconify,
dockerhubPushToken: pushToken, // used for pushing sconified image on user repo
yourWalletPublicAddress: walletAddress,
sconeVersion: DEFAULT_SCONE_VERSION,
}),
})
.catch(() => {
throw Error("Can't reach TEE transformation server!");
})
.then((res) => {
if (res.ok) {
return res.json().catch(() => {
// failed to parse body
throw Error('Unexpected server response');
});
}
if (res.status === 429) {
throw new TooManyRequestsError(
'TEE transformation server is busy, retry later'
);
}
// try getting error message from json body
return res
.json()
.catch(() => {
// failed to parse body
throw Error('Unknown server error');
initCallback: (ws) => {
ws.send(
serializeData({
type: 'REQUEST',
target: 'SCONIFY_BUILD', // call sconify handler
template,
dockerhubImageToSconify: iAppNameToSconify,
dockerhubPushToken: pushToken,
yourWalletPublicAddress: walletAddress,
sconeVersion: DEFAULT_SCONE_VERSION,
})
.then(({ error }) => {
throw Error(error || 'Unknown server error');
});
});
}
);
},
errorCallback: reject,
});
});

// Extract necessary information
if (!sconifyResult.dockerImage) {
Expand Down