diff --git a/.buildkite/eslint_flow_jest.yml b/.buildkite/eslint_flow_jest.yml --- a/.buildkite/eslint_flow_jest.yml +++ b/.buildkite/eslint_flow_jest.yml @@ -2,6 +2,8 @@ - label: ':eslint: :jest: ESLint & Flow & Jest' command: - '(pkill flow || true)' + - 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y' + - '. /root/.cargo/env' - 'yarn cleaninstall --frozen-lockfile --skip-optional --network-timeout 180000' - 'yarn eslint --max-warnings=0 & yarn workspace lib flow & yarn workspace web flow & yarn workspace landing flow & yarn workspace native flow & yarn workspace keyserver flow' - 'yarn workspace lib test && yarn workspace keyserver test' diff --git a/.buildkite/ios.yml b/.buildkite/ios.yml --- a/.buildkite/ios.yml +++ b/.buildkite/ios.yml @@ -2,7 +2,11 @@ - label: ':ios: iOS Build' command: - 'pod repo update && yarn workspace native clean-ios' - - 'yarn cleaninstall --frozen-lockfile --skip-optional --network-timeout 180000' + - 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y' + - 'source /Users/comm/.cargo/env' + - 'yarn cleaninstall --frozen-lockfile --skip-optional' - 'cd native/ios && xcodebuild -workspace Comm.xcworkspace -scheme Comm -destination generic/platform=iOS -allowProvisioningUpdates' + env: + PROTOC: "/opt/homebrew/bin/protoc" agents: - 'mac=true' diff --git a/keyserver/Dockerfile b/keyserver/Dockerfile --- a/keyserver/Dockerfile +++ b/keyserver/Dockerfile @@ -70,7 +70,16 @@ RUN mkdir /home/comm/backups #------------------------------------------------------------------------------- -# STEP 4: SET UP NVM +# STEP 4: SET UP CARGO (RUST PACKAGE MANAGER) +# We use Cargo to build pre-compiled Node.js addons in Rust +#------------------------------------------------------------------------------- + +# Install Rust and add Cargo's bin directory to the $PATH environment variable +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH /home/comm/.cargo/bin:$PATH + +#------------------------------------------------------------------------------- +# STEP 5: SET UP NVM # We use nvm to make sure we're running the right Node version #------------------------------------------------------------------------------- @@ -86,7 +95,7 @@ RUN cd keyserver && . bash/source-nvm.sh #------------------------------------------------------------------------------- -# STEP 5: YARN CLEANINSTALL +# STEP 6: YARN CLEANINSTALL # We run yarn cleaninstall before copying most of the files in for build caching #------------------------------------------------------------------------------- @@ -98,6 +107,13 @@ COPY --chown=comm native/package.json native/.flowconfig native/ COPY --chown=comm landing/package.json landing/.flowconfig landing/ COPY --chown=comm desktop/package.json desktop/ +COPY --chown=comm keyserver/addons/opaque-ke-napi/package.json \ + keyserver/addons/opaque-ke-napi/ + +# Create empty Rust library and copy in Cargo.toml file +RUN cargo init keyserver/addons/opaque-ke-napi --lib +COPY --chown=comm keyserver/addons/opaque-ke-napi/Cargo.toml \ + keyserver/addons/opaque-ke-napi/ # Copy in files needed for patch-package COPY --chown=comm patches patches/ @@ -106,7 +122,7 @@ RUN yarn cleaninstall #------------------------------------------------------------------------------- -# STEP 6: WEBPACK BUILD +# STEP 7: WEBPACK BUILD # We do this first so Docker doesn't rebuild when only keyserver files change #------------------------------------------------------------------------------- @@ -118,14 +134,21 @@ RUN yarn workspace web prod #------------------------------------------------------------------------------- -# STEP 7: COPY IN SOURCE FILES +# STEP 8: COPY IN SOURCE FILES # We run this later so the above layers are cached if only source files change #------------------------------------------------------------------------------- COPY --chown=comm . . #------------------------------------------------------------------------------- -# STEP 8: RUN BUILD SCRIPTS +# STEP 9: BUILD NODE ADDON +# Now that source files have been copied in, build the opaque-ke-napi addon +#------------------------------------------------------------------------------- + +RUN yarn workspace opaque-ke-napi build + +#------------------------------------------------------------------------------- +# STEP 10: RUN BUILD SCRIPTS # We need to populate keyserver/dist, among other things #------------------------------------------------------------------------------- @@ -133,7 +156,7 @@ RUN yarn workspace keyserver prod-build #------------------------------------------------------------------------------- -# STEP 9: RUN THE SERVER +# STEP 11: RUN THE SERVER # Actually run the Node.js keyserver using nvm #------------------------------------------------------------------------------- diff --git a/keyserver/addons/opaque-ke-napi/.gitignore b/keyserver/addons/opaque-ke-napi/.gitignore new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/.gitignore @@ -0,0 +1,3 @@ +Cargo.lock +target +napi diff --git a/keyserver/addons/opaque-ke-napi/Cargo.toml b/keyserver/addons/opaque-ke-napi/Cargo.toml new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/Cargo.toml @@ -0,0 +1,19 @@ +[package] +edition = "2021" +name = "opaque-ke-napi" +version = "0.1.0" +license = "BSD-3-Clause" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix +napi = { version = "2.10.1", default-features = false, features = ["napi4"] } +napi-derive = { version = "2.9.1", default-features = false } + +[build-dependencies] +napi-build = "2.0.1" + +[profile.release] +lto = true diff --git a/keyserver/addons/opaque-ke-napi/build.rs b/keyserver/addons/opaque-ke-napi/build.rs new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/keyserver/addons/opaque-ke-napi/index.js b/keyserver/addons/opaque-ke-napi/index.js new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/index.js @@ -0,0 +1,35 @@ +// @flow + +const { platform, arch } = process; + +type RustAPI = { + +sum: (a: number, b: number) => number, +}; + +async function getRustAPI(): Promise { + let nativeBinding = null; + if (platform === 'darwin' && arch === 'x64') { + // $FlowFixMe + nativeBinding = await import('./napi/opaque-ke-napi.darwin-x64.node'); + } else if (platform === 'darwin' && arch === 'arm64') { + // $FlowFixMe + nativeBinding = await import('./napi/opaque-ke-napi.darwin-arm64.node'); + } else if (platform === 'linux' && arch === 'x64') { + // $FlowFixMe + nativeBinding = await import('./napi/opaque-ke-napi.linux-x64-gnu.node'); + } else if (platform === 'linux' && arch === 'arm64') { + // $FlowFixMe + nativeBinding = await import('./napi/opaque-ke-napi.linux-arm64-gnu.node'); + } else { + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`); + } + + if (!nativeBinding) { + throw new Error('Failed to load native binding'); + } + + const { sum } = nativeBinding.default; + return { sum }; +} + +export { getRustAPI }; diff --git a/keyserver/addons/opaque-ke-napi/package.json b/keyserver/addons/opaque-ke-napi/package.json new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/package.json @@ -0,0 +1,33 @@ +{ + "name": "opaque-ke-napi", + "version": "0.0.1", + "main": "index.js", + "type": "module", + "napi": { + "name": "opaque-ke-napi", + "triples": { + "defaults": false, + "additional": [ + "x86_64-apple-darwin", + "aarch64-apple-darwin", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu" + ] + } + }, + "license": "BSD-3-Clause", + "devDependencies": { + "@napi-rs/cli": "^2.13.0" + }, + "engines": { + "node": ">= 16" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "napi build --platform napi --release", + "build:debug": "napi build --platform napi", + "version": "napi version", + "postinstall": "yarn build", + "clean": "rm -rf target/ && rm -rf napi/ && rm -rf node_modules/" + } +} diff --git a/keyserver/addons/opaque-ke-napi/src/lib.rs b/keyserver/addons/opaque-ke-napi/src/lib.rs new file mode 100644 --- /dev/null +++ b/keyserver/addons/opaque-ke-napi/src/lib.rs @@ -0,0 +1,9 @@ +#![deny(clippy::all)] + +#[macro_use] +extern crate napi_derive; + +#[napi] +pub fn sum(a: i32, b: i32) -> i32 { + a + b +} diff --git a/keyserver/loader.mjs b/keyserver/loader.mjs --- a/keyserver/loader.mjs +++ b/keyserver/loader.mjs @@ -1,16 +1,21 @@ // @flow -const localPackages = ['landing', 'lib', 'web']; +const localPackages = { + landing: 'landing', + lib: 'lib', + web: 'web', + ['opaque-ke-napi']: 'keyserver/addons/opaque-ke-napi', +}; async function resolve(specifier, context, nextResolve) { const defaultResult = await nextResolve(specifier, context); - // Special hack to use Babel-transpiled lib and web - if (localPackages.some(pkg => specifier.startsWith(`${pkg}/`))) { - const url = defaultResult.url.replace( - specifier, - `keyserver/dist/${specifier}`, - ); + for (const pkg in localPackages) { + if (specifier !== pkg && !specifier.startsWith(`${pkg}/`)) { + continue; + } + const path = localPackages[pkg]; + const url = defaultResult.url.replace(path, `keyserver/dist/${pkg}`); return { url }; } diff --git a/keyserver/package.json b/keyserver/package.json --- a/keyserver/package.json +++ b/keyserver/package.json @@ -8,7 +8,7 @@ "scripts": { "clean": "rm -rf dist/ && rm -rf node_modules/ && mkdir dist", "babel-build": ". bash/source-nvm.sh && yarn --silent babel src/ --out-dir dist/ --config-file ./babel.config.cjs --verbose --ignore 'src/landing/flow-typed','src/landing/node_modules','src/landing/package.json','src/lib/flow-typed','src/lib/node_modules','src/lib/package.json','src/web/flow-typed','src/web/node_modules','src/web/package.json','src/web/dist','src/web/webpack.config.js','src/web/account-bar.react.js','src/web/app.react.js','src/web/calendar','src/web/chat','src/web/flow','src/web/loading-indicator.react.js','src/web/modals','src/web/root.js','src/web/router-history.js','src/web/script.js','src/web/selectors/chat-selectors.js','src/web/selectors/entry-selectors.js','src/web/splash','src/web/vector-utils.js','src/web/vectors.react.js'", - "rsync": "rsync -rLpmuv --exclude '*/package.json' --exclude '*/node_modules/*' --include '*.json' --include '*.cjs' --exclude '*.*' src/ dist/", + "rsync": "rsync -rLpmuv --exclude '*/package.json' --exclude '*/node_modules/*' --include '*.json' --include '*.cjs' --include '*.node' --exclude '*.*' src/ dist/", "prod-build": "yarn babel-build && yarn rsync && yarn update-geoip", "update-geoip": "yarn script dist/scripts/update-geoip.js", "prod": "node --trace-warnings --experimental-json-modules --loader=./loader.mjs --experimental-specifier-resolution=node dist/keyserver", @@ -62,6 +62,7 @@ "mysql2": "^2.3.3", "node-schedule": "^2.1.0", "nodemailer": "^6.6.1", + "opaque-ke-napi": "0.0.1", "react": "17.0.2", "react-dom": "17.0.2", "react-html-email": "^3.0.0", diff --git a/keyserver/src/opaque-ke-napi b/keyserver/src/opaque-ke-napi new file mode 120000 --- /dev/null +++ b/keyserver/src/opaque-ke-napi @@ -0,0 +1 @@ +../addons/opaque-ke-napi \ No newline at end of file diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -8,10 +8,10 @@ "keyserver", "landing", "desktop", - "keyserver/addons/opaque-ke-node" + "keyserver/addons/opaque-ke-napi" ], "scripts": { - "clean": "yarn workspace lib clean && yarn workspace web clean && yarn workspace native clean && yarn workspace keyserver clean && yarn workspace landing clean && yarn workspace desktop clean && rm -rf node_modules/", + "clean": "yarn workspace lib clean && yarn workspace web clean && yarn workspace native clean && yarn workspace keyserver clean && yarn workspace landing clean && yarn workspace desktop clean && yarn workspace opaque-ke-napi clean && rm -rf node_modules/", "cleaninstall": "(killall flow || pkill flow || true) && yarn clean && yarn", "eslint": "eslint .", "eslint:fix": "eslint --fix .", diff --git a/yarn.lock b/yarn.lock --- a/yarn.lock +++ b/yarn.lock @@ -3106,6 +3106,11 @@ resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== +"@napi-rs/cli@^2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@napi-rs/cli/-/cli-2.13.0.tgz#227f1b63edc6fb9364317e4719884b4451d147b6" + integrity sha512-8U6TLh2PYXM2SX7HnpRBCqlPU48M9tRe0TLlb4qgUx61bt6PT6Qtdeho3e0ila70fnrbqCA6dnJWrbgbJIopcQ== + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": version "2.1.8-no-fsevents" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b" @@ -6764,11 +6769,6 @@ dependencies: rsvp "^4.8.4" -cargo-cp-artifact@^0.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.7.tgz#1181b9d6e71f00f17c068c05e3cd1b0864783341" - integrity sha512-pxEV9p1on8vu3BOKstVisF9TwMyGKCBRvzaVpQHuU2sLULCKrn3MJWx/4XlNzmG6xNCTPf78DJ7WCGgr2mOzjg== - chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"