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
131 changes: 120 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ on:
branches:
- main
- dev
push:
branches:
- main
- dev
env:
SUPPORT_DOCKERFILE_PATH: crates/support/Dockerfile
CIPHERNODE_DOCKERFILE_PATH: crates/Dockerfile
Expand All @@ -33,7 +29,92 @@ permissions:
packages: write

jobs:
detect_changes:
runs-on: 'ubuntu-latest'
outputs:
rust_tests: ${{ steps.jobs.outputs.rust_tests }}
ciphernode_e2e: ${{ steps.jobs.outputs.ciphernode_e2e }}
crisp: ${{ steps.jobs.outputs.crisp }}
templates: ${{ steps.jobs.outputs.templates }}
zk: ${{ steps.jobs.outputs.zk }}
contracts: ${{ steps.jobs.outputs.contracts }}
docker_support: ${{ steps.jobs.outputs.docker_support }}
docker_ciphernode: ${{ steps.jobs.outputs.docker_ciphernode }}
net: ${{ steps.jobs.outputs.net }}
init: ${{ steps.jobs.outputs.init }}
build_sdk: ${{ steps.jobs.outputs.build_sdk }}
build_e3_support_dev: ${{ steps.jobs.outputs.build_e3_support_dev }}
build_circuits: ${{ steps.jobs.outputs.build_circuits }}
integration_prebuild: ${{ steps.jobs.outputs.integration_prebuild }}
zk_prover_integration: ${{ steps.jobs.outputs.zk_prover_integration }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
rust:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
contracts:
- 'packages/enclave-contracts/**'
circuits:
- 'circuits/**'
- 'scripts/*-circuits.sh'
crisp:
- 'examples/CRISP/**'
templates:
- 'templates/**'
sdk:
- 'packages/enclave-sdk/**'
- 'packages/enclave-react/**'
- 'packages/enclave-config/**'
- 'crates/wasm/**'
integration_tests:
- 'tests/integration/**'
docker:
- 'crates/Dockerfile'
- 'crates/support/Dockerfile'
- 'crates/support/**'
ci:
- '.github/**'
- name: Compute job filters
id: jobs
run: |
RUST="${{ steps.filter.outputs.rust }}"
CONTRACTS="${{ steps.filter.outputs.contracts }}"
CIRCUITS="${{ steps.filter.outputs.circuits }}"
CRISP="${{ steps.filter.outputs.crisp }}"
TEMPLATES="${{ steps.filter.outputs.templates }}"
SDK="${{ steps.filter.outputs.sdk }}"
INTEGRATION="${{ steps.filter.outputs.integration_tests }}"
DOCKER="${{ steps.filter.outputs.docker }}"
CI="${{ steps.filter.outputs.ci }}"
FORCE="${{ github.event_name == 'workflow_dispatch' }}"

any() { for v in "$@"; do [ "$v" = "true" ] && echo "true" && return; done; echo "false"; }

echo "rust_tests=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $CI)" >> $GITHUB_OUTPUT
echo "ciphernode_e2e=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $INTEGRATION $CI)" >> $GITHUB_OUTPUT
echo "build_sdk=$(any $FORCE $RUST $CONTRACTS $SDK $INTEGRATION $CIRCUITS $CI $TEMPLATES)" >> $GITHUB_OUTPUT
echo "crisp=$(any $FORCE $CRISP $CONTRACTS $CIRCUITS $RUST $CI)" >> $GITHUB_OUTPUT
echo "templates=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT
echo "zk=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT
echo "contracts=$(any $FORCE $CONTRACTS $CI)" >> $GITHUB_OUTPUT
echo "docker_support=$(any $FORCE $DOCKER $CI)" >> $GITHUB_OUTPUT
echo "docker_ciphernode=$(any $FORCE $RUST $CONTRACTS $DOCKER $CI)" >> $GITHUB_OUTPUT
echo "net=$(any $FORCE $INTEGRATION $CI)" >> $GITHUB_OUTPUT
echo "init=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $CI)" >> $GITHUB_OUTPUT
echo "build_e3_support_dev=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT
echo "build_circuits=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT
echo "integration_prebuild=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $INTEGRATION $CI)" >> $GITHUB_OUTPUT
echo "zk_prover_integration=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT
Comment thread
ctrlc03 marked this conversation as resolved.
Comment thread
ctrlc03 marked this conversation as resolved.

rust_tests:
needs: [detect_changes]
if: needs.detect_changes.outputs.rust_tests == 'true'
runs-on:
group: enclave-ci
labels: [enclave-ci-runner]
Expand Down Expand Up @@ -81,6 +162,8 @@ jobs:
run: 'cargo test --test integration -- --nocapture'

zk_prover_integration:
needs: [detect_changes]
if: needs.detect_changes.outputs.zk_prover_integration == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -110,6 +193,8 @@ jobs:
run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture'

build_e3_support_risc0:
needs: [detect_changes]
if: needs.detect_changes.outputs.docker_support == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -147,6 +232,8 @@ jobs:
type=gha,mode=max,scope=e3-support

build_ciphernode_image:
needs: [detect_changes]
if: needs.detect_changes.outputs.docker_ciphernode == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -187,6 +274,8 @@ jobs:
type=gha,mode=max,scope=ciphernode

test_contracts:
needs: [detect_changes]
if: needs.detect_changes.outputs.contracts == 'true'
runs-on: 'ubuntu-latest'
steps:
- name: 'Check out the repo'
Expand Down Expand Up @@ -222,6 +311,8 @@ jobs:
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY

test_net:
needs: [detect_changes]
if: needs.detect_changes.outputs.net == 'true'
runs-on: 'ubuntu-latest'
steps:
- name: 'Check out the repo'
Expand All @@ -235,6 +326,8 @@ jobs:
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY

integration_prebuild:
needs: [detect_changes]
if: needs.detect_changes.outputs.integration_prebuild == 'true'
runs-on: 'ubuntu-latest'
steps:
- name: 'Check out the repo'
Expand Down Expand Up @@ -291,13 +384,15 @@ jobs:
if-no-files-found: error

ciphernode_integration_test:
needs: [integration_prebuild, build_enclave_cli, build_sdk]
needs: [detect_changes, integration_prebuild, build_enclave_cli, build_sdk]
if: needs.detect_changes.outputs.ciphernode_e2e == 'true'
runs-on:
group: enclave-ci
labels: [enclave-ci-runner]
strategy:
matrix:
test-suite: [base, persist]
# TODO removed base test for now
test-suite: [persist]
fail-fast: false
steps:
- name: 'Check out the repo'
Expand Down Expand Up @@ -355,6 +450,7 @@ jobs:
echo "## Test results for ${{ matrix.test-suite }}" >> $GITHUB_STEP_SUMMARY
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY

# Always runs — shared artifact dependency for many downstream jobs
build_enclave_cli:
runs-on: 'ubuntu-latest'
steps:
Expand Down Expand Up @@ -387,8 +483,9 @@ jobs:
retention-days: 1

crisp_unit:
needs: [detect_changes, build_crisp_sdk]
if: needs.detect_changes.outputs.crisp == 'true'
runs-on: 'ubuntu-latest'
needs: [build_crisp_sdk]
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -460,10 +557,11 @@ jobs:
run: 'pnpm test:contracts'

crisp_e2e:
needs: [detect_changes, build_enclave_cli, build_crisp_sdk]
if: needs.detect_changes.outputs.crisp == 'true'
runs-on:
group: enclave-ci
labels: [enclave-ci-runner]
needs: [build_enclave_cli, build_crisp_sdk]
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -573,6 +671,8 @@ jobs:
retention-days: 30

build_circuits:
needs: [detect_changes]
if: needs.detect_changes.outputs.build_circuits == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -626,8 +726,9 @@ jobs:
if-no-files-found: error

zk_prover_e2e:
needs: [detect_changes, build_circuits]
if: needs.detect_changes.outputs.zk == 'true'
runs-on: 'ubuntu-latest'
needs: [build_circuits]
steps:
- uses: actions/checkout@v6

Expand Down Expand Up @@ -689,6 +790,8 @@ jobs:
run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture

build_e3_support_dev:
needs: [detect_changes]
if: needs.detect_changes.outputs.build_e3_support_dev == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -718,6 +821,8 @@ jobs:
if-no-files-found: error

build_sdk:
needs: [detect_changes]
if: needs.detect_changes.outputs.build_sdk == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -774,6 +879,8 @@ jobs:
if-no-files-found: warn

build_crisp_sdk:
needs: [detect_changes]
if: needs.detect_changes.outputs.crisp == 'true'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -821,10 +928,11 @@ jobs:
if-no-files-found: warn

template_integration:
needs: [detect_changes, build_enclave_cli, build_e3_support_dev, build_sdk]
if: needs.detect_changes.outputs.templates == 'true'
runs-on:
group: enclave-ci
labels: [enclave-ci-runner]
needs: [build_enclave_cli, build_e3_support_dev, build_sdk]
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -895,8 +1003,9 @@ jobs:
pnpm test:integration

test_enclave_init:
needs: [detect_changes, build_enclave_cli, build_e3_support_dev]
if: needs.detect_changes.outputs.init == 'true'
runs-on: 'ubuntu-latest'
needs: [build_enclave_cli, build_e3_support_dev]
steps:
- name: Install pnpm
uses: pnpm/action-setup@v4
Expand Down
59 changes: 49 additions & 10 deletions templates/default/tests/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,43 @@ type E3StateOutputPublished = E3Shared & {
type E3State = E3StateRequested | E3StatePublished | E3StateOutputPublished

async function setupEventListeners(sdk: EnclaveSDK, store: Map<bigint, E3State>) {
async function waitForEvent<T extends AllEventTypes>(type: T, trigger?: () => Promise<void>): Promise<EnclaveEvent<T>> {
async function waitForEvent<T extends AllEventTypes>(
type: T,
trigger?: () => Promise<void>,
timeoutMs?: number,
): Promise<EnclaveEvent<T>> {
return new Promise((resolve, reject) => {
sdk.once(type, resolve).catch(reject)
let settled = false
let timer: ReturnType<typeof setTimeout> | undefined

const handler = (event: EnclaveEvent<T>) => {
if (settled) return
settled = true
if (timer !== undefined) clearTimeout(timer)
sdk.off(type, handler)
resolve(event)
}

const fail = (err: unknown) => {
if (settled) return
settled = true
if (timer !== undefined) clearTimeout(timer)
sdk.off(type, handler)
reject(err)
}

// Use onEnclaveEvent so `handler` is the actual registered reference
// (sdk.once wraps in an internal closure, making sdk.off unable to remove it)
sdk.onEnclaveEvent(type, handler).catch(fail)

if (timeoutMs !== undefined) {
timer = setTimeout(() => {
fail(new Error(`Timed out waiting for event: ${type} after ${timeoutMs}ms`))
}, timeoutMs)
}

if (trigger) {
trigger().catch(reject)
trigger().catch(fail)
}
})
}
Expand Down Expand Up @@ -102,6 +134,7 @@ async function setupEventListeners(sdk: EnclaveSDK, store: Map<bigint, E3State>)

await sdk.onEnclaveEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, (event) => {
const id = event.data.e3Id

const state = store.get(id)

if (!state) {
Expand Down Expand Up @@ -156,7 +189,7 @@ describe('Integration', () => {
const { waitForEvent } = await setupEventListeners(sdk, store)

const threshold: [number, number] = [DEFAULT_E3_CONFIG.threshold_min, DEFAULT_E3_CONFIG.threshold_max]
const duration = 250
const duration = 600
const inputWindow = await calculateInputWindow(publicClient, duration)
const thresholdBfvParams = await sdk.getThresholdBfvParamsSet()
const e3ProgramParams = encodeBfvParams(thresholdBfvParams)
Expand Down Expand Up @@ -189,10 +222,16 @@ describe('Integration', () => {
await new Promise((resolve) => setTimeout(resolve, 1000))

// REQUEST phase
await waitForEvent(EnclaveEventType.E3_REQUESTED, async () => {
console.log('Requested E3...')
await sdk.requestE3(requestParams)
})
const timeoutMs = duration * 1000

await waitForEvent(
EnclaveEventType.E3_REQUESTED,
async () => {
console.log('Requested E3...')
await sdk.requestE3(requestParams)
},
timeoutMs,
)

state = store.get(0n)
assert(state, 'store should have E3State but it was falsey')
Expand All @@ -205,7 +244,7 @@ describe('Integration', () => {
assert.strictEqual(stageAfterRequest, E3Stage.Requested, 'E3 stage should be Requested after requestE3')

// Ciphernodes will publish a public key within the COMMITTEE_PUBLISHED event
event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED)
event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED, undefined, timeoutMs)

const publicKeyBytes = hexToBytes(event.data.publicKey as `0x${string}`)

Expand Down Expand Up @@ -244,7 +283,7 @@ describe('Integration', () => {
)
await sdk.waitForTransaction(txHash)

const plaintextEvent = await waitForEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED)
const plaintextEvent = await waitForEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, undefined, timeoutMs)

const result = decodePlaintextOutput(plaintextEvent.data.plaintextOutput)
assert(result !== null, 'Failed to decode plaintext output')
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/fns.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ strip_ansi() {

waiton() {
local file_path="$1"
local timeout="${2:-600}" # default 10 minutes
local start_time=$(date +%s)
until [ -f "$file_path" ]; do
if [ $(($(date +%s) - start_time)) -ge $timeout ]; then
echo "Timeout after ${timeout}s waiting for: $file_path" >&2
return 1
fi
sleep 1
done
}
Expand Down
Loading