From 7ee94866dedc8dd4fe6832ede26f3018d8df8bc9 Mon Sep 17 00:00:00 2001 From: Ertugrul Aypek Date: Wed, 8 Oct 2025 14:44:55 +0200 Subject: [PATCH 1/5] Add pre-commit hooks and gitleaks pipeline --- .envrc | 2 +- .github/dependabot.yml | 2 +- .../build-and-push-upgrade-hawk.yaml | 2 +- .github/workflows/build-and-push.yaml | 1 - .github/workflows/check-secrets.yml | 23 + .github/workflows/prod-deploy-approvals.yml | 30 +- ...mp-branch-build-and-push-upgrade-hawk.yaml | 3 +- .github/workflows/test-gpu.yaml | 2 +- .pre-commit-config.yaml | 65 ++ Dockerfile.genesis.dev.hawk | 2 +- README.md | 9 + SECURITY.md | 2 +- deploy/README.md | 1 - deploy/dev/README.md | 2 +- .../dev/ampc-hnsw-0-dev/values-ampc-hnsw.yaml | 2 +- deploy/e2e/iris-mpc-0.yaml.tpl | 2 +- deploy/e2e/iris-mpc-1.yaml.tpl | 2 +- deploy/prod/common-values-ampc-hnsw.yaml | 2 +- deploy/prod/common-values-reshare-server.yaml | 22 +- .../smpcv2-0-prod/values-reshare-server.yaml | 4 +- .../smpcv2-1-prod/values-reshare-server.yaml | 4 +- .../smpcv2-2-prod/values-reshare-server.yaml | 4 +- deploy/stage/common-values-ampc-hnsw.yaml | 1 - .../stage/common-values-reshare-server.yaml | 22 +- .../smpcv2-0-stage/values-reshare-server.yaml | 4 +- .../smpcv2-1-stage/values-reshare-server.yaml | 4 +- .../smpcv2-2-stage/values-reshare-server.yaml | 4 +- docker-compose.dev.yaml | 2 +- iris-mpc-common/Cargo.toml | 1 - iris-mpc-common/src/bin/README.md | 2 +- iris-mpc-common/src/bin/data/iris_codes.json | 2 +- .../src/example-data/all_rotations.txt | 2 +- .../src/example-data/flipped_codes.txt | 4 +- .../src/example-data/random_codes.txt | 4 +- .../src/sage/gr4_encoding_example.sage | 274 +++---- .../src/sage/symmetric-gr-shamir_sage.sage | 684 +++++++++--------- iris-mpc-cpu/.gitignore | 2 +- iris-mpc-cpu/src/proto/party_node.proto | 2 +- iris-mpc-gpu/src/dot/kernel.cu | 6 +- iris-mpc-py/examples-py/.gitignore | 2 +- iris-mpc-py/examples-py/test_integration.py | 2 +- iris-mpc-py/pyproject.toml | 2 +- iris-mpc-upgrade/src/bin/.gitignore | 2 +- iris-mpc-upgrade/src/bin/Dockerfile | 2 +- iris-mpc-upgrade/src/bin/nginx/nginx.conf | 2 +- .../src/bin/reshare-protocol-local.sh | 1 - iris-mpc-upgrade/src/bin/ssh_chain.sh | 2 +- iris-mpc-utils/.gitignore | 2 +- iris-mpc/bin/server.rs | 4 +- justfile | 6 +- migrations/20240729175942_results.down.sql | 2 +- migrations/20240729175942_results.up.sql | 2 +- migrations/20241121084719_remove_sequence.sql | 2 +- ...250313141530_modifications_table_v2.up.sql | 6 +- .../20250521000000_binary_graph_fmt.down.sql | 2 +- rustfmt.toml | 1 - .../db-cleaner-helper-pod-ampc-hnsw.yaml | 1 - scripts/purge_stage/purge_stage_dbs.sh | 12 +- scripts/setup-pre-commit.sh | 41 ++ scripts/tools/init-servers.sh | 1 - 60 files changed, 717 insertions(+), 588 deletions(-) create mode 100644 .github/workflows/check-secrets.yml create mode 100644 .pre-commit-config.yaml create mode 100755 scripts/setup-pre-commit.sh diff --git a/.envrc b/.envrc index fe86e11f4..9e2f4f934 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -dotenv_if_exists .test.env \ No newline at end of file +dotenv_if_exists .test.env diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bdbb6657b..70b707b54 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,7 +13,7 @@ updates: # Used to get an immediate test time: "05:10" timezone: "Etc/UTC" - + - package-ecosystem: "github-actions" # See documentation for possible values directory: "/" # Location of package manifests schedule: diff --git a/.github/workflows/build-and-push-upgrade-hawk.yaml b/.github/workflows/build-and-push-upgrade-hawk.yaml index 4ab182f09..ab74f2ee5 100644 --- a/.github/workflows/build-and-push-upgrade-hawk.yaml +++ b/.github/workflows/build-and-push-upgrade-hawk.yaml @@ -4,7 +4,7 @@ on: push: branches: - main - + release: types: - 'published' diff --git a/.github/workflows/build-and-push.yaml b/.github/workflows/build-and-push.yaml index 3c0e63fe8..3910d7f0e 100644 --- a/.github/workflows/build-and-push.yaml +++ b/.github/workflows/build-and-push.yaml @@ -94,4 +94,3 @@ jobs: --private-key $WEB_PRIVATE_KEY \ --rpc-url https://worldchain-sepolia.g.alchemy.com/public \ "addAllowedHash(bytes32)" ${DIGEST:7} - diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml new file mode 100644 index 000000000..8968107f2 --- /dev/null +++ b/.github/workflows/check-secrets.yml @@ -0,0 +1,23 @@ +name: Check secret leaks + +on: + pull_request: + push: + branches: + - main + +jobs: + check-secret-leaks: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + with: + fetch-depth: 0 # Needed for gitleaks to scan full history + + - name: Run Gitleaks v8.21.2 on full history + run: | + docker run --rm -v $(pwd):/repo ghcr.io/gitleaks/gitleaks:a9e1950fe247fbb08817393121691474c55a6cfa \ + detect --source /repo --verbose --no-git diff --git a/.github/workflows/prod-deploy-approvals.yml b/.github/workflows/prod-deploy-approvals.yml index e451c3198..e72bc545b 100644 --- a/.github/workflows/prod-deploy-approvals.yml +++ b/.github/workflows/prod-deploy-approvals.yml @@ -20,17 +20,17 @@ jobs: script: | // Configuration - modify these values as needed const protectedPath = 'deploy/prod'; - + // Define approval groups - each group must have at least one approval approversGroupA = [ - 'philsippl', - 'eaypek-tfh', - 'carlomazzaferro', - 'wojciechsromek', - 'leonanos8', + 'philsippl', + 'eaypek-tfh', + 'carlomazzaferro', + 'wojciechsromek', + 'leonanos8', 'danielle-tfh' ]; - + approversGroupB = [ 'camelop', // UC Berkeley 'stneng', // UC Berkeley @@ -43,16 +43,16 @@ jobs: 'SyrineSlim', // FAU 'sebhlr' // FAU ]; - + const approvalGroups = new Map([ ['Group A', approversGroupA], ['Group B', approversGroupB] ]); - + const pull_number = context.payload.pull_request.number; const { owner, repo } = context.repo; core.info(`Checking PR #${pull_number} in ${owner}/${repo}`); - + const files = await github.paginate(github.rest.pulls.listFiles, { owner, repo, @@ -61,12 +61,12 @@ jobs: core.info(`Found ${files.length} changed files`); const fileNames = files.map(f => f.filename).join(', '); core.info(`Changed files: ${fileNames}`); - + const touchesProdDeploy = files.some((file) => file.filename === protectedPath || file.filename.startsWith(`${protectedPath}/`) ); core.info(`Touches ${protectedPath}: ${touchesProdDeploy}`); - + if (!touchesProdDeploy) { core.info(`No changes in ${protectedPath} detected. Skipping approval requirements.`); return; @@ -110,14 +110,14 @@ jobs: // Check approvals for each group const groupApprovalStatus = new Map(); const groupApprovers = new Map(); - + for (const [groupName, members] of approvalGroups) { const approvers = approvedLogins.filter((login) => members.includes(login)); const hasApproval = approvers.length > 0; - + groupApprovers.set(groupName, approvers); groupApprovalStatus.set(groupName, hasApproval); - + core.info(`${groupName} approvers: [${approvers.join(', ')}] - Has approval: ${hasApproval}`); } diff --git a/.github/workflows/temp-branch-build-and-push-upgrade-hawk.yaml b/.github/workflows/temp-branch-build-and-push-upgrade-hawk.yaml index e612d674b..410905c09 100644 --- a/.github/workflows/temp-branch-build-and-push-upgrade-hawk.yaml +++ b/.github/workflows/temp-branch-build-and-push-upgrade-hawk.yaml @@ -1,4 +1,4 @@ -name: Branch - Hawk Upgrade Build and push docker image +name: Branch - Hawk Upgrade Build and push docker image on: push: @@ -47,4 +47,3 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max file: Dockerfile.genesis.hawk - diff --git a/.github/workflows/test-gpu.yaml b/.github/workflows/test-gpu.yaml index 2f06aeaca..09a63aa79 100644 --- a/.github/workflows/test-gpu.yaml +++ b/.github/workflows/test-gpu.yaml @@ -59,7 +59,7 @@ jobs: run: sudo apt-get update && sudo apt-get install -y pkg-config libssl-dev protobuf-compiler - name: Install CUDA and NCCL dependencies - if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && + if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && steps.cache-cuda-nccl.outputs.cache-hit != 'true' env: DEBIAN_FRONTEND: noninteractive diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..6c9b3076d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,65 @@ +# Pre-commit hooks configuration +# See https://pre-commit.com for more information + +repos: + # General pre-commit hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: 2c9f875913ee60ca25ce70243dc24d5b6415598c # v4.6.0 + hooks: + - id: trailing-whitespace + exclude: ^(.*\.md|.*\.sage)$ + - id: end-of-file-fixer + - id: check-yaml + args: ['--unsafe'] # Allow custom tags in k8s yaml files + - id: check-toml + - id: check-added-large-files + args: ['--maxkb=1000'] + - id: check-merge-conflict + - id: check-case-conflict + - id: mixed-line-ending + args: ['--fix=lf'] + - id: detect-private-key + + # Gitleaks - Secret detection + - repo: https://github.com/gitleaks/gitleaks + rev: a9e1950fe247fbb08817393121691474c55a6cfa # v8.21.2 + hooks: + - id: gitleaks + + # Rust formatting + - repo: local + hooks: + - id: cargo-fmt + name: cargo fmt + description: Format Rust code with rustfmt + entry: cargo fmt + language: system + types: [rust] + pass_filenames: false + + - id: cargo-clippy + name: cargo clippy + description: Lint Rust code with clippy + entry: cargo clippy + args: ['--all-targets', '--all-features', '--', '-D', 'warnings'] + language: system + types: [rust] + pass_filenames: false + + - id: cargo-check + name: cargo check + description: Check Rust code compiles + entry: cargo check + args: ['--all-targets', '--all-features'] + language: system + types: [rust] + pass_filenames: false + + - id: cargo-doc + name: cargo doc + description: Check Rust documentation + entry: cargo doc + args: ['--no-deps', '--document-private-items'] + language: system + types: [rust] + pass_filenames: false diff --git a/Dockerfile.genesis.dev.hawk b/Dockerfile.genesis.dev.hawk index a743a449f..8445b866c 100644 --- a/Dockerfile.genesis.dev.hawk +++ b/Dockerfile.genesis.dev.hawk @@ -56,4 +56,4 @@ COPY scripts/run-server-docker.sh . USER 65534 -ENTRYPOINT ["./run-server-docker.sh", "genesis"] +ENTRYPOINT ["./run-server-docker.sh", "genesis"] diff --git a/README.md b/README.md index c03896822..8eb682d43 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,15 @@ We welcome contributions to this project! Please follow these guidelines when co - Include appropriate tests for new functionality - Update documentation as needed +### Pre-commit hooks +We use pre-commit hooks to ensure our changes adhere to the project's standards. Please set up pre-commit hooks by running: +```bash +./scripts/setup-pre-commit.sh +pre-commit run --all-files +``` +git config --global commit.gpgsign true +``` + ## How to release New releases are created automagically by [Release Drafter GH action](https://github.com/worldcoin//gpu-iris-mpc/actions/workflows/release.yaml). diff --git a/SECURITY.md b/SECURITY.md index 62d8a3309..b9a23b7e8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -30,4 +30,4 @@ Please report security bugs in third-party software to the original maintainers. ### Responsible Disclosure -We believe that responsible disclosure is a net benefit for the community and subsequently encourage researchers to publish their findings after the issues have been remediated. We do ask, however, that you allow sufficient time for patches to be deployed globally, so please coordinate with Worldcoin PSIRT prior to publishing, either through the bug bounty program or over email. For more information on responsible disclosure, please see Google Project Zeroโ€™s [Vulnerability Disclosure policy](https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-policy.html) as an example. \ No newline at end of file +We believe that responsible disclosure is a net benefit for the community and subsequently encourage researchers to publish their findings after the issues have been remediated. We do ask, however, that you allow sufficient time for patches to be deployed globally, so please coordinate with Worldcoin PSIRT prior to publishing, either through the bug bounty program or over email. For more information on responsible disclosure, please see Google Project Zeroโ€™s [Vulnerability Disclosure policy](https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-policy.html) as an example. diff --git a/deploy/README.md b/deploy/README.md index abd1e6d4a..81fab495d 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -79,4 +79,3 @@ After updating the image URL in all necessary files, commit the changes and crea 6. Once the PR is approved, merge it into the main branch. 7. After merging the PR, ArgoCD will automatically detect the configuration updates within 5 minutes, pull the updated configuration, and start the upgrade process. - diff --git a/deploy/dev/README.md b/deploy/dev/README.md index 77ed3d38b..4cea9c7a8 100644 --- a/deploy/dev/README.md +++ b/deploy/dev/README.md @@ -146,4 +146,4 @@ cargo run --release --bin client -- \ --response-queue-url https://sqs.eu-central-1.amazonaws.com/238407200320/hnsw-smpc-results.fifo \ --n-batches 1 \ --batch-size 5 -``` \ No newline at end of file +``` diff --git a/deploy/dev/ampc-hnsw-0-dev/values-ampc-hnsw.yaml b/deploy/dev/ampc-hnsw-0-dev/values-ampc-hnsw.yaml index 7308ea2fb..2fdc52a8f 100644 --- a/deploy/dev/ampc-hnsw-0-dev/values-ampc-hnsw.yaml +++ b/deploy/dev/ampc-hnsw-0-dev/values-ampc-hnsw.yaml @@ -187,7 +187,7 @@ env: - name: SMPC__TLS__CLIENT_ONLY_TLS value: "false" - + initContainer: enabled: true image: "amazon/aws-cli:2.17.62" diff --git a/deploy/e2e/iris-mpc-0.yaml.tpl b/deploy/e2e/iris-mpc-0.yaml.tpl index 011102c23..a51033106 100644 --- a/deploy/e2e/iris-mpc-0.yaml.tpl +++ b/deploy/e2e/iris-mpc-0.yaml.tpl @@ -263,7 +263,7 @@ iris-mpc-0: - name : SMPC__ENABLE_DEBUG_TIMING value: "true" - + - name : SMPC__FULL_SCAN_SIDE_SWITCHING_ENABLED value: "false" diff --git a/deploy/e2e/iris-mpc-1.yaml.tpl b/deploy/e2e/iris-mpc-1.yaml.tpl index 5cf33d2d9..b3e4c7c0b 100644 --- a/deploy/e2e/iris-mpc-1.yaml.tpl +++ b/deploy/e2e/iris-mpc-1.yaml.tpl @@ -245,7 +245,7 @@ iris-mpc-1: - name: SMPC__FIXED_SHARED_SECRETS value: "true" - + - name: SMPC__NODE_HOSTNAMES value: '["iris-mpc-0.$ENV.svc.cluster.local","iris-mpc-1.$ENV.svc.cluster.local","iris-mpc-2.$ENV.svc.cluster.local"]' diff --git a/deploy/prod/common-values-ampc-hnsw.yaml b/deploy/prod/common-values-ampc-hnsw.yaml index 8d79521e9..8776cd692 100644 --- a/deploy/prod/common-values-ampc-hnsw.yaml +++ b/deploy/prod/common-values-ampc-hnsw.yaml @@ -122,7 +122,7 @@ nginxSidecar: # Run nginx as non-root user - use /tmp for PID file pid /tmp/nginx.pid; worker_processes auto; - + # Send error logs to stderr error_log /dev/stderr info; diff --git a/deploy/prod/common-values-reshare-server.yaml b/deploy/prod/common-values-reshare-server.yaml index 55d8af900..d0fe153fe 100644 --- a/deploy/prod/common-values-reshare-server.yaml +++ b/deploy/prod/common-values-reshare-server.yaml @@ -90,53 +90,53 @@ nginxSidecar: config: nginx.conf: | worker_processes auto; - + error_log /dev/stderr notice; pid /tmp/nginx.pid; - + events { worker_connections 1024; } - + http { proxy_temp_path /tmp/proxy_temp; client_body_temp_path /tmp/client_temp; fastcgi_temp_path /tmp/fastcgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; scgi_temp_path /tmp/scgi_temp; - + log_format basic '$remote_addr [$time_local] ' '$status $bytes_sent'; - + server { listen 6443 ssl; http2 on; - + ssl_certificate /etc/nginx/cert/certificate.crt; ssl_certificate_key /etc/nginx/cert/key.pem; - + ssl_protocols TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; - + # Enable session resumption to improve performance ssl_session_cache shared:SSL:10m; ssl_session_timeout 1h; client_max_body_size 100M; - + location / { # Forward gRPC traffic to the gRPC server on port 7000 grpc_pass grpc://127.0.0.1:7000; error_page 502 = /error502grpc; # Custom error page for GRPC backend issues } - + # Custom error page location = /error502grpc { internal; default_type text/plain; return 502 "Bad Gateway: gRPC server unreachable."; } - + access_log /dev/stdout basic; } } diff --git a/deploy/prod/smpcv2-0-prod/values-reshare-server.yaml b/deploy/prod/smpcv2-0-prod/values-reshare-server.yaml index 9c45b48f8..f657ed54d 100644 --- a/deploy/prod/smpcv2-0-prod/values-reshare-server.yaml +++ b/deploy/prod/smpcv2-0-prod/values-reshare-server.yaml @@ -35,7 +35,7 @@ initContainer: # Set up environment variables HOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name "$PARTY_ID".smpcv2.worldcoin.org --query "HostedZones[].Id" --output text) - + # Generate the JSON content in memory BATCH_JSON=$(cat < - mock_mask_code = np.stack([mock_mask_code, mock_mask_code], axis=-1) + mock_mask_code = np.stack([mock_mask_code, mock_mask_code], axis=-1) return IrisTemplate(iris_codes=[mock_iris_code], mask_codes=[mock_mask_code], iris_code_version="v2.1") def encode_template(template): @@ -34,4 +34,4 @@ matcher = HammingDistanceMatcher(0) print(matcher.run(t1, t2)) # 15 rotations both directions matcher = HammingDistanceMatcher(15) -print(matcher.run(t1, t2)) \ No newline at end of file +print(matcher.run(t1, t2)) diff --git a/iris-mpc-common/src/sage/gr4_encoding_example.sage b/iris-mpc-common/src/sage/gr4_encoding_example.sage index 2ab587069..e70961562 100644 --- a/iris-mpc-common/src/sage/gr4_encoding_example.sage +++ b/iris-mpc-common/src/sage/gr4_encoding_example.sage @@ -1,137 +1,137 @@ -### COMPUTED BASIS MATRICES ### - -# 16-bit encoding -M16 = MatrixSpace(Integers(2**16), 4, 4) -V16 = M16.column_space() -S16 = M16([ - [ 1, 0, 0, 0 ], - [ 58082, 1, 0, 0 ], - [ 60579, 25194, 1, 0 ], - [ 17325, 51956, 57011, 1 ], -]) -# TODO this one is wrong for the new matrices... -_lin_comb_16 = V16([ - 1, - 50642, - 57413, - 17471 -]) # only for reference, not required explicitly by protocol - -# 64-bit encoding; matrix can be used for any lower bit count b by taking the -# entry-wise remainders mod 2^b -M64 = MatrixSpace(Integers(2**64), 4, 4) -V64 = M64.column_space() -S64 = M64([ - [ 1, 0, 0, 0 ], - [ 3721680401061044962, 1, 0, 0 ], - [ 8289148916157705379, 3107565179763450474, 1, 0 ], - [ 11432970333055501229, 7744929693175761652, 17484058852862123699, 1 ], -]) -# TODO this one is wrong for the new matrices... -_lin_comb_64 = V64([ - 1, - 13808594730259957202, - 12520909974947291205, - 12689163005896508479 -]) # only for reference, not required explicitly by protocol -assert(M16(S64) == S16) -assert(V16(_lin_comb_64) == _lin_comb_16) - - -### DEMONSTRATION ### - -# Change-of-basis matrix -S = S16 - -# Dimension 4 vector space (free module actually) for Galois ring elements -V = V16 - -# Specific linear combination appearing "under the hood" in the construction -_lin_comb = _lin_comb_16 - - -# We will be working in the usual Galois ring R = (ZZ/2^mZZ)[x] / (x^4 - x - 1) - -def prod_R_monom(a, b): - """ - Return the product of two elements of R, represented as coefficient - vectors in the monomial basis. - """ - a0, a1, a2, a3 = a - b0, b1, b2, b3 = b - R = a0.base_ring() - - return vector(R, [ - a3*b1 + a2*b2 + a1*b3 + a0*b0, - a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1, - a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2, - a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3, - ]) - - -# Three bases of R are involved in our computations. They are: -# 1. The monomial basis -# 2. The basis A with change of basis matrix S to the monomial basis -# 3. The basis B with change of basis matrix S*S.transpose() to the monomial basis - -change_of_basis_A_to_monom = S -change_of_basis_monom_to_B = (S*S.transpose()).inverse() -print(change_of_basis_A_to_monom) -print() -print(change_of_basis_A_to_monom.inverse()) -print() -print(change_of_basis_monom_to_B) -print() - -# - Vectors are first encoded as elements of R represented in basis A -# - For vectors so encoded, their dot product can be computed as the -# first coordinate of their R-product when represented in basis B -# - This first product coordinate can be efficiently computed concretely by -# representing one element in the monomial basis and the other in basis B, -# and then taking the dot product of these vector representations - -# The following demonstrates several ways of computing the inner product of -# two vectors using elements of R encoded in the same way, in terms of basis -# A, by applying appropriate basis changes. - -# Initial vectors in basis A -u_A = V([1,1,1,1]) -v_A = V.random_element() - -# 1. Plaintext dot product of input vectors -ip1 = u_A.dot_product(v_A) - -# 2. Specific linear combination of product coefficients in the monomial basis -u_monom = change_of_basis_A_to_monom * u_A -print() -print(u_A) -print(u_monom) -print() -v_monom = change_of_basis_A_to_monom * v_A -uv_monom = prod_R_monom(u_monom, v_monom) -ip2 = _lin_comb.dot_product(uv_monom) -# TODO fix the linear combinations to make this work -# assert(ip2 == ip1) - -# 3. First coordinate of GR4 product in basis B -uv_B = change_of_basis_monom_to_B * uv_monom -ip3 = uv_B[0] -assert(ip3 == ip1) - -# 4. First coordinate of GR4 product in basis B, computed as the dot product -# of vectors represented in the monomial basis and in basis B, respectively -v_B = change_of_basis_monom_to_B * v_monom -ip4 = u_monom.dot_product(v_B) -assert(ip4 == ip1) - -print("All assertions passed.") - - -# For secret sharing, one should be able to convert the ring elements to the -# monomial basis to produce the secret shares for different parties as usual, -# and then "query" shares can remain in the monomial basis, while "database" -# shares are converted to basis B. Then the dot product of query and -# database shares gives the analog of the "constant coefficient in the -# monomial basis" used in the original protocol, but here the MPC parties are -# able to produce the database shares themselves since it is accomplished by -# just a change of basis from the query shares. +### COMPUTED BASIS MATRICES ### + +# 16-bit encoding +M16 = MatrixSpace(Integers(2**16), 4, 4) +V16 = M16.column_space() +S16 = M16([ + [ 1, 0, 0, 0 ], + [ 58082, 1, 0, 0 ], + [ 60579, 25194, 1, 0 ], + [ 17325, 51956, 57011, 1 ], +]) +# TODO this one is wrong for the new matrices... +_lin_comb_16 = V16([ + 1, + 50642, + 57413, + 17471 +]) # only for reference, not required explicitly by protocol + +# 64-bit encoding; matrix can be used for any lower bit count b by taking the +# entry-wise remainders mod 2^b +M64 = MatrixSpace(Integers(2**64), 4, 4) +V64 = M64.column_space() +S64 = M64([ + [ 1, 0, 0, 0 ], + [ 3721680401061044962, 1, 0, 0 ], + [ 8289148916157705379, 3107565179763450474, 1, 0 ], + [ 11432970333055501229, 7744929693175761652, 17484058852862123699, 1 ], +]) +# TODO this one is wrong for the new matrices... +_lin_comb_64 = V64([ + 1, + 13808594730259957202, + 12520909974947291205, + 12689163005896508479 +]) # only for reference, not required explicitly by protocol +assert(M16(S64) == S16) +assert(V16(_lin_comb_64) == _lin_comb_16) + + +### DEMONSTRATION ### + +# Change-of-basis matrix +S = S16 + +# Dimension 4 vector space (free module actually) for Galois ring elements +V = V16 + +# Specific linear combination appearing "under the hood" in the construction +_lin_comb = _lin_comb_16 + + +# We will be working in the usual Galois ring R = (ZZ/2^mZZ)[x] / (x^4 - x - 1) + +def prod_R_monom(a, b): + """ + Return the product of two elements of R, represented as coefficient + vectors in the monomial basis. + """ + a0, a1, a2, a3 = a + b0, b1, b2, b3 = b + R = a0.base_ring() + + return vector(R, [ + a3*b1 + a2*b2 + a1*b3 + a0*b0, + a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1, + a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2, + a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3, + ]) + + +# Three bases of R are involved in our computations. They are: +# 1. The monomial basis +# 2. The basis A with change of basis matrix S to the monomial basis +# 3. The basis B with change of basis matrix S*S.transpose() to the monomial basis + +change_of_basis_A_to_monom = S +change_of_basis_monom_to_B = (S*S.transpose()).inverse() +print(change_of_basis_A_to_monom) +print() +print(change_of_basis_A_to_monom.inverse()) +print() +print(change_of_basis_monom_to_B) +print() + +# - Vectors are first encoded as elements of R represented in basis A +# - For vectors so encoded, their dot product can be computed as the +# first coordinate of their R-product when represented in basis B +# - This first product coordinate can be efficiently computed concretely by +# representing one element in the monomial basis and the other in basis B, +# and then taking the dot product of these vector representations + +# The following demonstrates several ways of computing the inner product of +# two vectors using elements of R encoded in the same way, in terms of basis +# A, by applying appropriate basis changes. + +# Initial vectors in basis A +u_A = V([1,1,1,1]) +v_A = V.random_element() + +# 1. Plaintext dot product of input vectors +ip1 = u_A.dot_product(v_A) + +# 2. Specific linear combination of product coefficients in the monomial basis +u_monom = change_of_basis_A_to_monom * u_A +print() +print(u_A) +print(u_monom) +print() +v_monom = change_of_basis_A_to_monom * v_A +uv_monom = prod_R_monom(u_monom, v_monom) +ip2 = _lin_comb.dot_product(uv_monom) +# TODO fix the linear combinations to make this work +# assert(ip2 == ip1) + +# 3. First coordinate of GR4 product in basis B +uv_B = change_of_basis_monom_to_B * uv_monom +ip3 = uv_B[0] +assert(ip3 == ip1) + +# 4. First coordinate of GR4 product in basis B, computed as the dot product +# of vectors represented in the monomial basis and in basis B, respectively +v_B = change_of_basis_monom_to_B * v_monom +ip4 = u_monom.dot_product(v_B) +assert(ip4 == ip1) + +print("All assertions passed.") + + +# For secret sharing, one should be able to convert the ring elements to the +# monomial basis to produce the secret shares for different parties as usual, +# and then "query" shares can remain in the monomial basis, while "database" +# shares are converted to basis B. Then the dot product of query and +# database shares gives the analog of the "constant coefficient in the +# monomial basis" used in the original protocol, but here the MPC parties are +# able to produce the database shares themselves since it is accomplished by +# just a change of basis from the query shares. diff --git a/iris-mpc-common/src/sage/symmetric-gr-shamir_sage.sage b/iris-mpc-common/src/sage/symmetric-gr-shamir_sage.sage index 59c9ae0dc..bea7ec582 100644 --- a/iris-mpc-common/src/sage/symmetric-gr-shamir_sage.sage +++ b/iris-mpc-common/src/sage/symmetric-gr-shamir_sage.sage @@ -1,342 +1,342 @@ -def main(): - test_sharing() - # test_gr_inv() - # test_gr_prod() - # test_lagrange() - # compute_lagrange_coeffs() - # GR_symbolic_inverse_formula() - print("done") - -######################################## - -def test_sharing(): - # degree of interpolating polynomials - k = 2 - - # Exceptional sequence in Galois ring - # First point in list is index of shared sequence - base_points = ( - GR.zero(), - GR.one(), - GR((0, 1, 0, 0)), - GR((1, 1, 0, 0)), - GR((1, 0, 1, 0)), - GR((0, 1, 1, 0)), - ) - - # Random vectors to compute inner products over - Coeffs = GR.coeff_ring - N = 10*GR.extension_degree # total length 40 - u = list(Coeffs.random_element() for _ in range(N)) - v = list(Coeffs.random_element() for _ in range(N)) - - plaintext_ip = sum(ui*vi for ui, vi in zip(u, v)) - - ### Protocol 11, Step 1 ### - - # Translate vectors to corresponding Galois ring elements in monomial basis - u_monom = encode_vec_symmetric(u) - v_monom = encode_vec_symmetric(v) - - # Set up parties of MPC - party_base_points = base_points[1:] - parties = tuple(InnerProductParty(x) for x in party_base_points) - - ### Protocol 11, Step 2 ### - - # Share Galois ring elements to parties - for ui in u_monom: - ui_shares = share_gr_elt(ui, k+1, base_points) - for p, sh in zip(parties, ui_shares): - p.u_shares.append(sh) - for vi in v_monom: - vi_shares = share_gr_elt(vi, k+1, base_points) - for p, sh in zip(parties, vi_shares): - p.v_shares.append(sh) - - # Print all parties and shares - for p in parties: - party_idx = party_base_points.index(p.base_point) - print(f"Party {party_idx+1}") - print(f"Eval point ({p.base_point})") - print("U shares:") - for sh in p.u_shares: - print(f" {sh}") - print("V shares:") - for sh in p.v_shares: - print(f" {sh}") - print() - - ### Protocol 11, Steps 3-6 ### - - # Parties compute shares of inner product - ip_shares = tuple( - p.compute_inner_product_share(party_base_points) for p in parties - ) - print("Additive IP shares:", *ip_shares) - - # Sum (additive) inner product shares to compute final inner product - mpc_ip = sum(ip_shares) - - print(f"Shares sum to plaintext inner product: {mpc_ip == plaintext_ip}") - assert(mpc_ip == plaintext_ip) - -def encode_vec_symmetric(u): - """ - Encode a list of values as Galois ring elements batching into blocks and - interpreting the block as a vector in terms of the precomputed symmetric - encoding basis. - """ - assert(GR.extension_degree == 4) - return list(map( - lambda u_block: change_of_basis_A_to_monom*GR(u_block), - batched(u, 4) - )) - -def share_gr_elt(elt, deg, base_points): - """ - Produce secret shares of degree `deg` sharing of elt over `base_points`. - - Takes place in the monomial basis. - """ - vals = (elt,) + tuple(GR.random_element() for _ in range(1, deg)) - shares = tuple(legrange_interp(base_points[:deg], p, vals) for p in base_points[1:]) - return shares - -class InnerProductParty: - """ - Class representing the data and operation of an MPC party for the Galois - ring inner product protocol. - """ - - def __init__(self, base_point): - self.base_point = base_point - self.u_shares = list() - self.v_shares = list() - - def compute_inner_product_share(self, base_points): - ### Protocol 11, Step 3 - base_point_idx = base_points.index(self.base_point) - shamir_coeff = legrange_coeffs(base_points, GR.zero())[base_point_idx] - - ### Protocol 11, Step 4 - u_premult = (GR.prod_monom(shamir_coeff, val) for val in self.u_shares) - u_premult_B = (change_of_basis_monom_to_B * ui for ui in u_premult) - - ### Protocol 11, Step 5 - products = list(val_u.dot_product(val_v) for val_u, val_v in zip(u_premult_B, self.v_shares)) - - ### Protocol 11, Step 6 - output_share = sum(products) - - return output_share - -######################################## - -class GaloisRing4: - extension_degree = 4 - - def __init__(self, modulus_bits): - self.extension_degree = GaloisRing4.extension_degree - self.modulus_bits = modulus_bits - self.coeff_ring = IntegerModRing(2**modulus_bits) - self.matrix_space = MatrixSpace(self.coeff_ring, self.extension_degree) - self.element_space = self.matrix_space.column_space() - - def __call__(self, vec): - return self.element_space(vec) - - def prod_monom(self, a, b): - """ - Return the product of two Galois ring elements represented in the - monomial basis. - """ - a0, a1, a2, a3 = a - b0, b1, b2, b3 = b - - # multiplication formula derived directly from ring definition - # monomial basis to monomial basis - return self.element_space(( - a3*b1 + a2*b2 + a1*b3 + a0*b0, - a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1, - a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2, - a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3, - )) - - def inv_monom(self, a): - """ - Return the inverse of a Galois ring element represented in the - monomial basis. - """ - if (all(ai % 2 == 0 for ai in a)): - raise ZeroDivisionError("inverse of %s does not exist" % a) - - a0, a1, a2, a3 = a - - # see: GR_symbolic_inverse_formula() for symbolic computation of these formulas - b0 = (a0^3 + a1^3 - a0*a2^2 + a2^3 + (3*a0 - 2*a1 + a2)*a3^2 + a3^3 - (3*a0*a1 - a1^2)*a2 + (3*a0^2 - 2*a0*a1 - 3*a1*a2 + a2^2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) - b1 = -(a0^2*a1 - (a0 - a1)*a2^2 + a3^3 + (a0*a1 - a1^2 - 2*a0*a2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) - b2 = (a0*a1^2 - a0^2*a2 + a2^3 - (a0 + 2*a1)*a2*a3 + a0*a3^2 + a3^3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) - b3 = -(a1^3 - 2*a0*a1*a2 + a2^3 + (2*a0 - a1)*a3^2 + a3^3 + (a0^2 - 3*a1*a2 + a2^2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) - - return self.element_space((b0, b1, b2, b3)) - - def zero(self): - return self.element_space.zero() - - def one(self): - return self.element_space((1, 0, 0, 0)) - - def random_element(self): - return self.element_space.random_element() - -# symmetric encoding matrix for up to 64 bits -M64 = MatrixSpace(Integers(2**64), 4, 4) -S64 = M64([ - [ 1, 0, 0, 0 ], - [ 3721680401061044962, 1, 0, 0 ], - [ 8289148916157705379, 3107565179763450474, 1, 0 ], - [ 11432970333055501229, 7744929693175761652, 17484058852862123699, 1 ], -]) - -######################################## - -GR = GaloisRing4(16) - -S = GR.matrix_space(S64) -change_of_basis_A_to_monom = S -change_of_basis_monom_to_B = (S*S.transpose()).inverse() - -######################################## - -def legrange_interp(base_points, z, values): - """ - Compute the value of the interpolating polynomial in GR[x] with - evaluations `values` at points `base_points`, computed at `z`. For - proper functioning, the ring elements in `base_points` should form - an "exceptional sequence", meaning any pair of distinct elements - should be invertible in GR. - """ - coeffs = legrange_coeffs(base_points, z) - val = GR.zero() - for coeff, y in zip(coeffs, values): - val += GR.prod_monom(coeff, y) - return val - -def legrange_coeffs(base_points, z): - """ - Returns a sequence of ring coefficients to multiply function evaluations - by to compute the polynomial interpolation at a fixed evaluation point. - - `base_points` is the sequence of distinct points for the interpolation - `eval` is the evaluation point for the interpolation. - """ - coeffs = list() - for i, p in enumerate(base_points): - coeff = GR.one() - for j, q in enumerate(base_points): - if j != i: - coeff = GR.prod_monom(coeff, z - q) - coeff = GR.prod_monom(coeff, GR.inv_monom(p - q)) - coeffs.append(coeff) - return coeffs - -def batched(iterable, n): - # batched('ABCDEFG', 3) --> ABC DEF G - from itertools import islice - if n < 1: - raise ValueError('n must be at least one') - it = iter(iterable) - while batch := tuple(islice(it, n)): - yield batch - -######################################## - -def compute_lagrange_coeffs(): - base_points = ( - GR.one(), - GR((0, 1, 0, 0)), - GR((1, 1, 0 ,0)), - ) - - coeffs = legrange_coeffs(base_points, GR.zero()) - print("Base points:") - for p in base_points: - print(p) - print() - print("Lagrange coefficients for evaluation at 0:") - for c in coeffs: - print(c) - -def GR_symbolic_inverse_formula(): - """ - Use Sage symbolic ring functionality to compute the formula for - inversion in the Galois ring GR = (Z/2^kZ)[x] / (x^4 - x - 1). - """ - - a = SR.var('a', 4) - a0, a1, a2, a3 = a - - b = SR.var('b', 4) - b0, b1, b2, b3 = b - - eqs = [ - a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3 == 0, - a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2 == 0, - a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1 == 0, - a3*b1 + a2*b2 + a1*b3 + a0*b0 == 1, - ] - - s, = solve(eqs, b, solution_dict=True) - - print("Symbolic formulas for inverses in Galois ring GR(2^k, 4)...\n") - - for key, value in s.items(): - print(f"{key}:") - print(value) - print() - -######################################## - -def test_gr_inv(): - a = GR((1, 1, 0, 0)) - a_inv = GR((2, -1, 1, -1)) - assert(GR.inv_monom(a) == a_inv) - -def test_gr_prod(): - a = GR((7, 0, 3, 0)) - b = GR((1, 1, 0, 1)) - ab = GR((7, 10, 6, 10)) - assert(GR.prod_monom(a, b) == ab) - -def test_lagrange(): - # degree two polynomial - x0 = GR.zero() - y0 = GR((3, 7, 1, 0)) - - x1 = GR.one() - y1 = GR((2, 2, 2, 0)) - - x2 = GR((0, 1, 0, 0)) - y2 = GR.zero() - - x3 = GR((1, 1, 0, 0)) - y3 = legrange_interp((x0, x1, x2), x3, (y0, y1, y2)) - - x4 = GR((1, 0, 1, 0)) - y4 = legrange_interp((x0, x1, x2), x4, (y0, y1, y2)) - - x5 = GR((0, 1, 1, 0)) - y5 = legrange_interp((x0, x1, x2), x5, (y0, y1, y2)) - - z0 = legrange_interp((x1, x2, x3), x0, (y1, y2, y3)) - assert(y0 == z0) - - z4 = legrange_interp((x0, x1, x3), x4, (y0, y1, y3)) - assert(y4 == z4) - - z5 = legrange_interp((x0, x1, x2, x3, x4), x5, (y0, y1, y2, y3, y4)) - assert(y5 == z5) - -if __name__ == '__main__': - sys.exit(main()) +def main(): + test_sharing() + # test_gr_inv() + # test_gr_prod() + # test_lagrange() + # compute_lagrange_coeffs() + # GR_symbolic_inverse_formula() + print("done") + +######################################## + +def test_sharing(): + # degree of interpolating polynomials + k = 2 + + # Exceptional sequence in Galois ring + # First point in list is index of shared sequence + base_points = ( + GR.zero(), + GR.one(), + GR((0, 1, 0, 0)), + GR((1, 1, 0, 0)), + GR((1, 0, 1, 0)), + GR((0, 1, 1, 0)), + ) + + # Random vectors to compute inner products over + Coeffs = GR.coeff_ring + N = 10*GR.extension_degree # total length 40 + u = list(Coeffs.random_element() for _ in range(N)) + v = list(Coeffs.random_element() for _ in range(N)) + + plaintext_ip = sum(ui*vi for ui, vi in zip(u, v)) + + ### Protocol 11, Step 1 ### + + # Translate vectors to corresponding Galois ring elements in monomial basis + u_monom = encode_vec_symmetric(u) + v_monom = encode_vec_symmetric(v) + + # Set up parties of MPC + party_base_points = base_points[1:] + parties = tuple(InnerProductParty(x) for x in party_base_points) + + ### Protocol 11, Step 2 ### + + # Share Galois ring elements to parties + for ui in u_monom: + ui_shares = share_gr_elt(ui, k+1, base_points) + for p, sh in zip(parties, ui_shares): + p.u_shares.append(sh) + for vi in v_monom: + vi_shares = share_gr_elt(vi, k+1, base_points) + for p, sh in zip(parties, vi_shares): + p.v_shares.append(sh) + + # Print all parties and shares + for p in parties: + party_idx = party_base_points.index(p.base_point) + print(f"Party {party_idx+1}") + print(f"Eval point ({p.base_point})") + print("U shares:") + for sh in p.u_shares: + print(f" {sh}") + print("V shares:") + for sh in p.v_shares: + print(f" {sh}") + print() + + ### Protocol 11, Steps 3-6 ### + + # Parties compute shares of inner product + ip_shares = tuple( + p.compute_inner_product_share(party_base_points) for p in parties + ) + print("Additive IP shares:", *ip_shares) + + # Sum (additive) inner product shares to compute final inner product + mpc_ip = sum(ip_shares) + + print(f"Shares sum to plaintext inner product: {mpc_ip == plaintext_ip}") + assert(mpc_ip == plaintext_ip) + +def encode_vec_symmetric(u): + """ + Encode a list of values as Galois ring elements batching into blocks and + interpreting the block as a vector in terms of the precomputed symmetric + encoding basis. + """ + assert(GR.extension_degree == 4) + return list(map( + lambda u_block: change_of_basis_A_to_monom*GR(u_block), + batched(u, 4) + )) + +def share_gr_elt(elt, deg, base_points): + """ + Produce secret shares of degree `deg` sharing of elt over `base_points`. + + Takes place in the monomial basis. + """ + vals = (elt,) + tuple(GR.random_element() for _ in range(1, deg)) + shares = tuple(legrange_interp(base_points[:deg], p, vals) for p in base_points[1:]) + return shares + +class InnerProductParty: + """ + Class representing the data and operation of an MPC party for the Galois + ring inner product protocol. + """ + + def __init__(self, base_point): + self.base_point = base_point + self.u_shares = list() + self.v_shares = list() + + def compute_inner_product_share(self, base_points): + ### Protocol 11, Step 3 + base_point_idx = base_points.index(self.base_point) + shamir_coeff = legrange_coeffs(base_points, GR.zero())[base_point_idx] + + ### Protocol 11, Step 4 + u_premult = (GR.prod_monom(shamir_coeff, val) for val in self.u_shares) + u_premult_B = (change_of_basis_monom_to_B * ui for ui in u_premult) + + ### Protocol 11, Step 5 + products = list(val_u.dot_product(val_v) for val_u, val_v in zip(u_premult_B, self.v_shares)) + + ### Protocol 11, Step 6 + output_share = sum(products) + + return output_share + +######################################## + +class GaloisRing4: + extension_degree = 4 + + def __init__(self, modulus_bits): + self.extension_degree = GaloisRing4.extension_degree + self.modulus_bits = modulus_bits + self.coeff_ring = IntegerModRing(2**modulus_bits) + self.matrix_space = MatrixSpace(self.coeff_ring, self.extension_degree) + self.element_space = self.matrix_space.column_space() + + def __call__(self, vec): + return self.element_space(vec) + + def prod_monom(self, a, b): + """ + Return the product of two Galois ring elements represented in the + monomial basis. + """ + a0, a1, a2, a3 = a + b0, b1, b2, b3 = b + + # multiplication formula derived directly from ring definition + # monomial basis to monomial basis + return self.element_space(( + a3*b1 + a2*b2 + a1*b3 + a0*b0, + a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1, + a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2, + a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3, + )) + + def inv_monom(self, a): + """ + Return the inverse of a Galois ring element represented in the + monomial basis. + """ + if (all(ai % 2 == 0 for ai in a)): + raise ZeroDivisionError("inverse of %s does not exist" % a) + + a0, a1, a2, a3 = a + + # see: GR_symbolic_inverse_formula() for symbolic computation of these formulas + b0 = (a0^3 + a1^3 - a0*a2^2 + a2^3 + (3*a0 - 2*a1 + a2)*a3^2 + a3^3 - (3*a0*a1 - a1^2)*a2 + (3*a0^2 - 2*a0*a1 - 3*a1*a2 + a2^2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) + b1 = -(a0^2*a1 - (a0 - a1)*a2^2 + a3^3 + (a0*a1 - a1^2 - 2*a0*a2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) + b2 = (a0*a1^2 - a0^2*a2 + a2^3 - (a0 + 2*a1)*a2*a3 + a0*a3^2 + a3^3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) + b3 = -(a1^3 - 2*a0*a1*a2 + a2^3 + (2*a0 - a1)*a3^2 + a3^3 + (a0^2 - 3*a1*a2 + a2^2)*a3)/(a0^4 + a0*a1^3 - a1^4 - 2*a0^2*a2^2 + (a0 - a1)*a2^3 + a2^4 + (a0 - a1 + a2)*a3^3 - a3^4 + (3*a0^2 - 5*a0*a1 + 2*a1^2 + 4*a0*a2)*a3^2 - (3*a0^2*a1 - 4*a0*a1^2)*a2 + (3*a0^3 - 4*a0^2*a1 + (a0 - 4*a1)*a2^2 - 3*(a0*a1 - a1^2)*a2)*a3) + + return self.element_space((b0, b1, b2, b3)) + + def zero(self): + return self.element_space.zero() + + def one(self): + return self.element_space((1, 0, 0, 0)) + + def random_element(self): + return self.element_space.random_element() + +# symmetric encoding matrix for up to 64 bits +M64 = MatrixSpace(Integers(2**64), 4, 4) +S64 = M64([ + [ 1, 0, 0, 0 ], + [ 3721680401061044962, 1, 0, 0 ], + [ 8289148916157705379, 3107565179763450474, 1, 0 ], + [ 11432970333055501229, 7744929693175761652, 17484058852862123699, 1 ], +]) + +######################################## + +GR = GaloisRing4(16) + +S = GR.matrix_space(S64) +change_of_basis_A_to_monom = S +change_of_basis_monom_to_B = (S*S.transpose()).inverse() + +######################################## + +def legrange_interp(base_points, z, values): + """ + Compute the value of the interpolating polynomial in GR[x] with + evaluations `values` at points `base_points`, computed at `z`. For + proper functioning, the ring elements in `base_points` should form + an "exceptional sequence", meaning any pair of distinct elements + should be invertible in GR. + """ + coeffs = legrange_coeffs(base_points, z) + val = GR.zero() + for coeff, y in zip(coeffs, values): + val += GR.prod_monom(coeff, y) + return val + +def legrange_coeffs(base_points, z): + """ + Returns a sequence of ring coefficients to multiply function evaluations + by to compute the polynomial interpolation at a fixed evaluation point. + + `base_points` is the sequence of distinct points for the interpolation + `eval` is the evaluation point for the interpolation. + """ + coeffs = list() + for i, p in enumerate(base_points): + coeff = GR.one() + for j, q in enumerate(base_points): + if j != i: + coeff = GR.prod_monom(coeff, z - q) + coeff = GR.prod_monom(coeff, GR.inv_monom(p - q)) + coeffs.append(coeff) + return coeffs + +def batched(iterable, n): + # batched('ABCDEFG', 3) --> ABC DEF G + from itertools import islice + if n < 1: + raise ValueError('n must be at least one') + it = iter(iterable) + while batch := tuple(islice(it, n)): + yield batch + +######################################## + +def compute_lagrange_coeffs(): + base_points = ( + GR.one(), + GR((0, 1, 0, 0)), + GR((1, 1, 0 ,0)), + ) + + coeffs = legrange_coeffs(base_points, GR.zero()) + print("Base points:") + for p in base_points: + print(p) + print() + print("Lagrange coefficients for evaluation at 0:") + for c in coeffs: + print(c) + +def GR_symbolic_inverse_formula(): + """ + Use Sage symbolic ring functionality to compute the formula for + inversion in the Galois ring GR = (Z/2^kZ)[x] / (x^4 - x - 1). + """ + + a = SR.var('a', 4) + a0, a1, a2, a3 = a + + b = SR.var('b', 4) + b0, b1, b2, b3 = b + + eqs = [ + a3*b3 + a3*b0 + a2*b1 + a1*b2 + a0*b3 == 0, + a3*b3 + a3*b2 + a2*b3 + a2*b0 + a1*b1 + a0*b2 == 0, + a3*b2 + a2*b3 + a3*b1 + a2*b2 + a1*b3 + a1*b0 + a0*b1 == 0, + a3*b1 + a2*b2 + a1*b3 + a0*b0 == 1, + ] + + s, = solve(eqs, b, solution_dict=True) + + print("Symbolic formulas for inverses in Galois ring GR(2^k, 4)...\n") + + for key, value in s.items(): + print(f"{key}:") + print(value) + print() + +######################################## + +def test_gr_inv(): + a = GR((1, 1, 0, 0)) + a_inv = GR((2, -1, 1, -1)) + assert(GR.inv_monom(a) == a_inv) + +def test_gr_prod(): + a = GR((7, 0, 3, 0)) + b = GR((1, 1, 0, 1)) + ab = GR((7, 10, 6, 10)) + assert(GR.prod_monom(a, b) == ab) + +def test_lagrange(): + # degree two polynomial + x0 = GR.zero() + y0 = GR((3, 7, 1, 0)) + + x1 = GR.one() + y1 = GR((2, 2, 2, 0)) + + x2 = GR((0, 1, 0, 0)) + y2 = GR.zero() + + x3 = GR((1, 1, 0, 0)) + y3 = legrange_interp((x0, x1, x2), x3, (y0, y1, y2)) + + x4 = GR((1, 0, 1, 0)) + y4 = legrange_interp((x0, x1, x2), x4, (y0, y1, y2)) + + x5 = GR((0, 1, 1, 0)) + y5 = legrange_interp((x0, x1, x2), x5, (y0, y1, y2)) + + z0 = legrange_interp((x1, x2, x3), x0, (y1, y2, y3)) + assert(y0 == z0) + + z4 = legrange_interp((x0, x1, x3), x4, (y0, y1, y3)) + assert(y4 == z4) + + z5 = legrange_interp((x0, x1, x2, x3, x4), x5, (y0, y1, y2, y3, y4)) + assert(y5 == z5) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/iris-mpc-cpu/.gitignore b/iris-mpc-cpu/.gitignore index 249cda967..3af0ccb68 100644 --- a/iris-mpc-cpu/.gitignore +++ b/iris-mpc-cpu/.gitignore @@ -1 +1 @@ -/data \ No newline at end of file +/data diff --git a/iris-mpc-cpu/src/proto/party_node.proto b/iris-mpc-cpu/src/proto/party_node.proto index 883ea6f5c..8d0c00a54 100644 --- a/iris-mpc-cpu/src/proto/party_node.proto +++ b/iris-mpc-cpu/src/proto/party_node.proto @@ -15,4 +15,4 @@ message SendRequests { repeated SendRequest requests = 1; } -message SendResponse {} \ No newline at end of file +message SendResponse {} diff --git a/iris-mpc-gpu/src/dot/kernel.cu b/iris-mpc-gpu/src/dot/kernel.cu index ad104232e..c36556e8e 100644 --- a/iris-mpc-gpu/src/dot/kernel.cu +++ b/iris-mpc-gpu/src/dot/kernel.cu @@ -94,7 +94,7 @@ extern "C" __global__ void openResults(unsigned long long *result1, unsigned lon } unsigned int matchCounter = atomicAdd(&partialResultsCounter[0], 1); - if (matchCounter < MAX_MATCHES_LEN * queryLength) + if (matchCounter < MAX_MATCHES_LEN * queryLength) { partialResultsQueryIndices[matchCounter] = queryIdx / ALL_ROTATIONS; partialResultsDbIndices[matchCounter] = dbIdx + offset; @@ -128,7 +128,7 @@ extern "C" __global__ void openResultsWithIndexMapping(unsigned long long *resul unsigned int dbIdx = indexMapping[chunkDbIdx]; unsigned int matchCounter = atomicAdd(&partialResultsCounter[0], 1); - if (matchCounter < MAX_MATCHES_LEN * queryLength) + if (matchCounter < MAX_MATCHES_LEN * queryLength) { partialResultsQueryIndices[matchCounter] = queryIdx / ALL_ROTATIONS; partialResultsDbIndices[matchCounter] = dbIdx; @@ -289,7 +289,7 @@ extern "C" __global__ void mergeDbResultsWithOrPolicyBitmap(unsigned long long * // else => (matchLeft && matchRight). bool finalMatch = useOr ? (matchLeft || matchRight) : (matchLeft && matchRight); - + if (finalMatch) { atomicMin(&finalResults[queryIdx], dbIdx); diff --git a/iris-mpc-py/examples-py/.gitignore b/iris-mpc-py/examples-py/.gitignore index 6320cd248..1269488f7 100644 --- a/iris-mpc-py/examples-py/.gitignore +++ b/iris-mpc-py/examples-py/.gitignore @@ -1 +1 @@ -data \ No newline at end of file +data diff --git a/iris-mpc-py/examples-py/test_integration.py b/iris-mpc-py/examples-py/test_integration.py index dc6d34bf6..0499ce0bd 100644 --- a/iris-mpc-py/examples-py/test_integration.py +++ b/iris-mpc-py/examples-py/test_integration.py @@ -59,4 +59,4 @@ i = random.randint(0, len(vertices)) ret = graph2.get_links(vertices[i], layer) if ret is not None: - print(f"Neighborhood of vertex {vertices[i]} in layer {layer}", ret) \ No newline at end of file + print(f"Neighborhood of vertex {vertices[i]} in layer {layer}", ret) diff --git a/iris-mpc-py/pyproject.toml b/iris-mpc-py/pyproject.toml index 8b731d0c3..6397d8053 100644 --- a/iris-mpc-py/pyproject.toml +++ b/iris-mpc-py/pyproject.toml @@ -13,4 +13,4 @@ classifiers = [ dynamic = ["version"] [tool.maturin] features = ["pyo3/extension-module"] -module-name = "iris_mpc_py" \ No newline at end of file +module-name = "iris_mpc_py" diff --git a/iris-mpc-upgrade/src/bin/.gitignore b/iris-mpc-upgrade/src/bin/.gitignore index 1c8b7cd18..b8af4b0c8 100644 --- a/iris-mpc-upgrade/src/bin/.gitignore +++ b/iris-mpc-upgrade/src/bin/.gitignore @@ -3,4 +3,4 @@ out1/ out2/ *.log cert/*.pem -cert/*.key \ No newline at end of file +cert/*.key diff --git a/iris-mpc-upgrade/src/bin/Dockerfile b/iris-mpc-upgrade/src/bin/Dockerfile index c5f83e6d8..b7412c9dd 100644 --- a/iris-mpc-upgrade/src/bin/Dockerfile +++ b/iris-mpc-upgrade/src/bin/Dockerfile @@ -1,3 +1,3 @@ FROM --platform=linux/amd64 ghcr.io/worldcoin/iris-mpc:146c2cae43dbeb586144d9d37d152a6b2bfacdd4 USER non-root -ENTRYPOINT "reshare-server --party-id 2 --sender1-party-id 0 --sender2-party-id 1 --bind-addr 0.0.0.0:7000 --environment testing --db-url postgres://postgres:postgres@new-db-4:6203 --batch-size 100 --healthcheck-port 3000" \ No newline at end of file +ENTRYPOINT "reshare-server --party-id 2 --sender1-party-id 0 --sender2-party-id 1 --bind-addr 0.0.0.0:7000 --environment testing --db-url postgres://postgres:postgres@new-db-4:6203 --batch-size 100 --healthcheck-port 3000" diff --git a/iris-mpc-upgrade/src/bin/nginx/nginx.conf b/iris-mpc-upgrade/src/bin/nginx/nginx.conf index c2031da3c..19c439f0d 100644 --- a/iris-mpc-upgrade/src/bin/nginx/nginx.conf +++ b/iris-mpc-upgrade/src/bin/nginx/nginx.conf @@ -47,4 +47,4 @@ http { error_page 502 = /error502grpc; # Custom error page for GRPC backend issues } } -} \ No newline at end of file +} diff --git a/iris-mpc-upgrade/src/bin/reshare-protocol-local.sh b/iris-mpc-upgrade/src/bin/reshare-protocol-local.sh index f41b52e7d..381eb57ed 100755 --- a/iris-mpc-upgrade/src/bin/reshare-protocol-local.sh +++ b/iris-mpc-upgrade/src/bin/reshare-protocol-local.sh @@ -40,4 +40,3 @@ AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1 A sleep 5 killall reshare-server - diff --git a/iris-mpc-upgrade/src/bin/ssh_chain.sh b/iris-mpc-upgrade/src/bin/ssh_chain.sh index ddd7c1fd3..9409aadef 100755 --- a/iris-mpc-upgrade/src/bin/ssh_chain.sh +++ b/iris-mpc-upgrade/src/bin/ssh_chain.sh @@ -18,4 +18,4 @@ openssl req -newkey rsa:4096 -nodes -keyout $CERT_PATH/server-key.pem -out $CERT openssl x509 -req -in $CERT_PATH/server-req.pem -days 60 -CA $CERT_PATH/ca-cert.pem -CAkey $CERT_PATH/ca-key.pem -CAcreateserial -out $CERT_PATH/server-cert.pem -extfile $CERT_PATH/server-ext.cnf echo "Server's signed certificate" -openssl x509 -in $CERT_PATH/server-cert.pem -noout -text \ No newline at end of file +openssl x509 -in $CERT_PATH/server-cert.pem -noout -text diff --git a/iris-mpc-utils/.gitignore b/iris-mpc-utils/.gitignore index 249cda967..3af0ccb68 100644 --- a/iris-mpc-utils/.gitignore +++ b/iris-mpc-utils/.gitignore @@ -1 +1 @@ -/data \ No newline at end of file +/data diff --git a/iris-mpc/bin/server.rs b/iris-mpc/bin/server.rs index ef3bb53b5..fc62a2219 100644 --- a/iris-mpc/bin/server.rs +++ b/iris-mpc/bin/server.rs @@ -1638,8 +1638,8 @@ async fn server_main(config: Config) -> Result<()> { let result_string = serde_json::to_string(&result_event) .expect("failed to serialize reset check result"); - // Mark the reset check modification as completed. - // Note that reset_check is only a query and does not persist anything into the database. + // Mark the reset check modification as completed. + // Note that reset_check is only a query and does not persist anything into the database. // We store modification so that the SNS result can be replayed. modifications .get_mut(&RequestId(reset_id)) diff --git a/justfile b/justfile index b8132f481..00e535e7d 100644 --- a/justfile +++ b/justfile @@ -1,8 +1,8 @@ -default: +default: just --list -dev-pg-up: +dev-pg-up: docker run --name gpu-iris-dev-db -d -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:16 -dev-pg-down: +dev-pg-down: docker stop gpu-iris-dev-db && docker rm gpu-iris-dev-db lint: cargo fmt --all -- --check diff --git a/migrations/20240729175942_results.down.sql b/migrations/20240729175942_results.down.sql index 9ceb26f1b..65b0cdbde 100644 --- a/migrations/20240729175942_results.down.sql +++ b/migrations/20240729175942_results.down.sql @@ -1 +1 @@ -DROP TABLE results; \ No newline at end of file +DROP TABLE results; diff --git a/migrations/20240729175942_results.up.sql b/migrations/20240729175942_results.up.sql index b687198a6..ccaaec836 100644 --- a/migrations/20240729175942_results.up.sql +++ b/migrations/20240729175942_results.up.sql @@ -1,4 +1,4 @@ CREATE TABLE results ( id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 0 MINVALUE 0) PRIMARY KEY, result_event TEXT NOT NULL -); \ No newline at end of file +); diff --git a/migrations/20241121084719_remove_sequence.sql b/migrations/20241121084719_remove_sequence.sql index b78151277..43e41f79e 100644 --- a/migrations/20241121084719_remove_sequence.sql +++ b/migrations/20241121084719_remove_sequence.sql @@ -1 +1 @@ -ALTER TABLE irises ALTER COLUMN id DROP IDENTITY IF EXISTS; \ No newline at end of file +ALTER TABLE irises ALTER COLUMN id DROP IDENTITY IF EXISTS; diff --git a/migrations/20250313141530_modifications_table_v2.up.sql b/migrations/20250313141530_modifications_table_v2.up.sql index de5e6142a..9fb1c9177 100644 --- a/migrations/20250313141530_modifications_table_v2.up.sql +++ b/migrations/20250313141530_modifications_table_v2.up.sql @@ -19,13 +19,13 @@ DECLARE BEGIN -- Lock the table to prevent race conditions LOCK TABLE modifications IN EXCLUSIVE MODE; - + -- Find the next available ID (max + 1 or 1 if table is empty) SELECT COALESCE(MAX(id) + 1, 1) INTO next_id FROM modifications; - + -- Assign the ID to the new row NEW.id := next_id; - + RETURN NEW; END; $$ LANGUAGE plpgsql; diff --git a/migrations/20250521000000_binary_graph_fmt.down.sql b/migrations/20250521000000_binary_graph_fmt.down.sql index 974134eda..9e0f34cb1 100644 --- a/migrations/20250521000000_binary_graph_fmt.down.sql +++ b/migrations/20250521000000_binary_graph_fmt.down.sql @@ -19,4 +19,4 @@ CREATE TABLE hawk_graph_entry graph_id integer NOT NULL, entry_point jsonb, CONSTRAINT hawk_graph_entry_pkey PRIMARY KEY (graph_id) -); \ No newline at end of file +); diff --git a/rustfmt.toml b/rustfmt.toml index e777a13ed..df02c1b2c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,3 @@ # See: https://github.com/rust-lang/rustfmt/blob/master/Configurations.md edition = "2021" use_field_init_shorthand = true - diff --git a/scripts/purge_stage/db-cleaner-helper-pod-ampc-hnsw.yaml b/scripts/purge_stage/db-cleaner-helper-pod-ampc-hnsw.yaml index 277b25fab..608a6dde0 100644 --- a/scripts/purge_stage/db-cleaner-helper-pod-ampc-hnsw.yaml +++ b/scripts/purge_stage/db-cleaner-helper-pod-ampc-hnsw.yaml @@ -21,4 +21,3 @@ spec: requests: cpu: 1 memory: 1Gi - diff --git a/scripts/purge_stage/purge_stage_dbs.sh b/scripts/purge_stage/purge_stage_dbs.sh index 1f73372e9..804506d4b 100755 --- a/scripts/purge_stage/purge_stage_dbs.sh +++ b/scripts/purge_stage/purge_stage_dbs.sh @@ -33,7 +33,7 @@ elif [ "$CLEANUP_TYPE" == "gpu" ]; then SECRET_NAME="stage/iris-mpc/rds-aurora-master-password" CLUSTER_NAME="smpcv2" NAMESPACE="iris-mpc" - echo "Using GPU secret: $SECRET_NAME" + echo "Using GPU secret: $SECRET_NAME" echo "Using GPU cluster name: $CLUSTER_NAME" echo "Using GPU namespace: $NAMESPACE" else @@ -88,29 +88,29 @@ clean_mpc_database() { CLUSTER="arn:aws:eks:eu-north-1:$ACCOUNT_ID:cluster/$CLUSTER_NAME-$PARTY_ID-stage" echo "Cleaning database for $PARTY_ID with cluster name $CLUSTER${SCHEMA_NAME_SUFFIX:+ and schema name suffix $SCHEMA_NAME_SUFFIX}" - + # Switch to the appropriate Kubernetes context kubectx $CLUSTER kubens $NAMESPACE - + # Create and use a temporary pod for database operations kubectl apply -f db-cleaner-helper-pod-$NAMESPACE.yaml echo "Waiting 10s for db-cleaner pod to be ready..." sleep 10 SCHEMA_NAME="SMPC${SCHEMA_NAME_SUFFIX}_stage_${PARTY_ID}" echo "Cleaning Database for URL: $DATABASE_URL with Schema name suffix: $SCHEMA_NAME" - + # Execute database cleanup commands kubectl exec -it db-cleaner -- bash -c "psql -H $DATABASE_URL -c 'SET search_path TO \"$SCHEMA_NAME\"; TRUNCATE irises RESTART IDENTITY;'" kubectl exec -it db-cleaner -- bash -c "psql -H $DATABASE_URL -c 'SET search_path TO \"$SCHEMA_NAME\"; TRUNCATE persistent_state RESTART IDENTITY;'" kubectl exec -it db-cleaner -- bash -c "psql -H $DATABASE_URL -c 'SET search_path TO \"$SCHEMA_NAME\"; TRUNCATE modifications RESTART IDENTITY;'" kubectl exec -it db-cleaner -- bash -c "psql -H $DATABASE_URL -c 'SET search_path TO \"$SCHEMA_NAME\"; TRUNCATE hawk_graph_entry RESTART IDENTITY;'" kubectl exec -it db-cleaner -- bash -c "psql -H $DATABASE_URL -c 'SET search_path TO \"$SCHEMA_NAME\"; TRUNCATE hawk_graph_links RESTART IDENTITY;'" - + # Clean up and restart deployment kubectl delete pod --force db-cleaner kubectl rollout restart deployment $NAMESPACE -n $NAMESPACE - + echo "Cleanup completed for $PARTY_ID" } diff --git a/scripts/setup-pre-commit.sh b/scripts/setup-pre-commit.sh new file mode 100755 index 000000000..6f1bed70a --- /dev/null +++ b/scripts/setup-pre-commit.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Setup script for pre-commit hooks + +set -e + +echo "๐Ÿ”ง Setting up pre-commit hooks..." + +# Check if pre-commit is installed +if ! command -v pre-commit &> /dev/null; then + echo "๐Ÿ“ฆ Installing pre-commit..." + + # Try to install with pip/pip3 + if command -v pip3 &> /dev/null; then + pip3 install pre-commit + elif command -v pip &> /dev/null; then + pip install pre-commit + elif command -v brew &> /dev/null; then + echo "Installing via Homebrew..." + brew install pre-commit + else + echo "โŒ Error: Could not find pip, pip3, or brew to install pre-commit" + echo "Please install pre-commit manually: https://pre-commit.com/#install" + exit 1 + fi +fi + +echo "โœ… pre-commit is installed" + +# Install the git hooks +echo "๐Ÿ“Œ Installing git hooks..." +pre-commit install + +# Install commit-msg hook +pre-commit install --hook-type commit-msg + +echo "๐ŸŽ‰ Pre-commit hooks installed successfully!" +echo "" +echo "To run hooks manually on all files: pre-commit run --all-files" +echo "To update hooks to latest versions: pre-commit autoupdate" +echo "" +echo "Hooks will now run automatically on every commit." diff --git a/scripts/tools/init-servers.sh b/scripts/tools/init-servers.sh index 6a080cca1..fd5af98fd 100755 --- a/scripts/tools/init-servers.sh +++ b/scripts/tools/init-servers.sh @@ -9,4 +9,3 @@ cargo run --bin key-manager -- --region us-east-1 --node-id 2 --env dev rotate cargo run --bin key-manager -- --region us-east-1 --node-id 0 --env dev rotate --public-key-bucket-name wf-dev-public-keys cargo run --bin key-manager -- --region us-east-1 --node-id 1 --env dev rotate --public-key-bucket-name wf-dev-public-keys cargo run --bin key-manager -- --region us-east-1 --node-id 2 --env dev rotate --public-key-bucket-name wf-dev-public-keys - From 9e54e3efeb59bede64e23831bb1d2732022b3518 Mon Sep 17 00:00:00 2001 From: Ertugrul Aypek Date: Wed, 8 Oct 2025 15:34:35 +0200 Subject: [PATCH 2/5] Add permissions to github actions to avoid security alerts --- .github/workflows/build-all-targets.yml | 2 ++ .github/workflows/check-licenses.yaml | 2 ++ .github/workflows/cpu-genesis-e2e-tests.yaml | 2 ++ .github/workflows/cpu-integration-tests.yaml | 2 ++ .github/workflows/doc.yml | 2 ++ .github/workflows/lint-rustfmt.yaml | 2 ++ .github/workflows/run-unit-tests.yaml | 2 ++ .github/workflows/test-gpu.yaml | 2 ++ 8 files changed, 16 insertions(+) diff --git a/.github/workflows/build-all-targets.yml b/.github/workflows/build-all-targets.yml index 3505620d5..2ba9cbe0c 100644 --- a/.github/workflows/build-all-targets.yml +++ b/.github/workflows/build-all-targets.yml @@ -13,6 +13,8 @@ concurrency: jobs: build-tests: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 diff --git a/.github/workflows/check-licenses.yaml b/.github/workflows/check-licenses.yaml index 64d17fb37..45c873f3b 100644 --- a/.github/workflows/check-licenses.yaml +++ b/.github/workflows/check-licenses.yaml @@ -8,6 +8,8 @@ on: jobs: cargo-deny: runs-on: ubuntu-24.04 + permissions: + contents: read steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0 - uses: EmbarkStudios/cargo-deny-action@f2ba7abc2abebaf185c833c3961145a3c275caad diff --git a/.github/workflows/cpu-genesis-e2e-tests.yaml b/.github/workflows/cpu-genesis-e2e-tests.yaml index 9a80b49b7..e61cd1867 100644 --- a/.github/workflows/cpu-genesis-e2e-tests.yaml +++ b/.github/workflows/cpu-genesis-e2e-tests.yaml @@ -15,6 +15,8 @@ jobs: timeout-minutes: 20 runs-on: labels: ubuntu-22.04-16core + permissions: + contents: read steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 diff --git a/.github/workflows/cpu-integration-tests.yaml b/.github/workflows/cpu-integration-tests.yaml index 555e762d6..169923288 100644 --- a/.github/workflows/cpu-integration-tests.yaml +++ b/.github/workflows/cpu-integration-tests.yaml @@ -15,6 +15,8 @@ jobs: timeout-minutes: 20 runs-on: labels: ubuntu-22.04-16core + permissions: + contents: read steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 009136a83..d9fcec72c 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -13,6 +13,8 @@ concurrency: jobs: doc: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 diff --git a/.github/workflows/lint-rustfmt.yaml b/.github/workflows/lint-rustfmt.yaml index 76ea47e72..1d0453741 100644 --- a/.github/workflows/lint-rustfmt.yaml +++ b/.github/workflows/lint-rustfmt.yaml @@ -10,6 +10,8 @@ concurrency: jobs: fmt: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 diff --git a/.github/workflows/run-unit-tests.yaml b/.github/workflows/run-unit-tests.yaml index d3b08c93c..1219d86a5 100644 --- a/.github/workflows/run-unit-tests.yaml +++ b/.github/workflows/run-unit-tests.yaml @@ -16,6 +16,8 @@ jobs: timeout-minutes: 20 runs-on: labels: ubuntu-22.04-16core + permissions: + contents: read services: postgres: diff --git a/.github/workflows/test-gpu.yaml b/.github/workflows/test-gpu.yaml index 09a63aa79..cb7b7d920 100644 --- a/.github/workflows/test-gpu.yaml +++ b/.github/workflows/test-gpu.yaml @@ -13,6 +13,8 @@ jobs: gpu-e2e: runs-on: arc-gpu-amd64-runner timeout-minutes: 25 + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 From ab108624ce67c1b64c639aba2303ee0051cac033 Mon Sep 17 00:00:00 2001 From: Ertugrul Aypek Date: Wed, 8 Oct 2025 15:37:49 +0200 Subject: [PATCH 3/5] run latest --- .github/workflows/check-secrets.yml | 4 ++-- .pre-commit-config.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml index 8968107f2..e0f80dbb0 100644 --- a/.github/workflows/check-secrets.yml +++ b/.github/workflows/check-secrets.yml @@ -17,7 +17,7 @@ jobs: with: fetch-depth: 0 # Needed for gitleaks to scan full history - - name: Run Gitleaks v8.21.2 on full history + - name: Run Gitleaks v8.28.0 on full history run: | - docker run --rm -v $(pwd):/repo ghcr.io/gitleaks/gitleaks:a9e1950fe247fbb08817393121691474c55a6cfa \ + docker run --rm -v $(pwd):/repo ghcr.io/gitleaks/gitleaks@sha256:cdbb7c955abce02001a9f6c9f602fb195b7fadc1e812065883f695d1eeaba854 \ detect --source /repo --verbose --no-git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c9b3076d..c31d0c331 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: # Gitleaks - Secret detection - repo: https://github.com/gitleaks/gitleaks - rev: a9e1950fe247fbb08817393121691474c55a6cfa # v8.21.2 + rev: 4fb43823ef3d152d239e92d7d5cb04783b548062 # v8.28.0 hooks: - id: gitleaks From 38a3d3021fde83f2ea58ebbfaa852980a08d3d99 Mon Sep 17 00:00:00 2001 From: Ertugrul Aypek Date: Thu, 9 Oct 2025 09:29:34 +0200 Subject: [PATCH 4/5] fix readme --- .github/workflows/check-secrets.yml | 2 -- README.md | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml index e0f80dbb0..da114d2d7 100644 --- a/.github/workflows/check-secrets.yml +++ b/.github/workflows/check-secrets.yml @@ -14,8 +14,6 @@ jobs: steps: - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - with: - fetch-depth: 0 # Needed for gitleaks to scan full history - name: Run Gitleaks v8.28.0 on full history run: | diff --git a/README.md b/README.md index 8eb682d43..70b429c88 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ We use pre-commit hooks to ensure our changes adhere to the project's standards. ./scripts/setup-pre-commit.sh pre-commit run --all-files ``` -git config --global commit.gpgsign true -``` ## How to release From 48f4600e2742b9ee16d6ec0faefb9aca0140182e Mon Sep 17 00:00:00 2001 From: Ertugrul Aypek Date: Thu, 9 Oct 2025 12:37:14 +0200 Subject: [PATCH 5/5] remove cargo check --- .pre-commit-config.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c31d0c331..239f4dbb7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,15 +46,6 @@ repos: types: [rust] pass_filenames: false - - id: cargo-check - name: cargo check - description: Check Rust code compiles - entry: cargo check - args: ['--all-targets', '--all-features'] - language: system - types: [rust] - pass_filenames: false - - id: cargo-doc name: cargo doc description: Check Rust documentation