Skip to content

Commit 296be78

Browse files
committed
feat(kms/auth-eth): migrate from Hardhat to Foundry
Migrates the KMS authorization smart contracts and bootAuth server from Hardhat to Foundry. The Solidity sources are functionally identical to master (pragma bumped ^0.8.22 → ^0.8.24 for OpenZeppelin 5.x, forge fmt applied); the ABI, events, and storage layout are byte-compatible with the live UUPS proxies on Phala mainnet. Stack changes: - Hardhat dependencies and config removed (hardhat.config.ts, typechain types, jest.integration config, all hardhat-bound .test.ts files, scripts/{deploy,upgrade,verify}.ts). - Foundry stack added: foundry.toml, three lib/ submodules pinned at forge-std v1.9.7, openzeppelin-contracts-upgradeable v5.4.0, and openzeppelin-foundry-upgrades v0.4.0; a Foundry .t.sol test suite (46 unit tests covering TCB toggle, factory deploy, upgrade paths, and storage compatibility from legacy 5-arg initializers); production deployment / management / query / upgrade scripts under script/. - BootAuth Fastify server retained byte-identical except src/ethereum.ts, which swaps typechain for a 4-method hand-written ABI (same struct, same selectors, functionally identical). - .openzeppelin/unknown-2035.json (proxy registry for the four live Phala-mainnet proxies) restored for historical reference. Operator-script fixes surfaced during a post-rebase audit: - script/Upgrade.s.sol previously only had UpgradeKmsToV2 / UpgradeAppToV2 pointing at test-only mock contracts. Added UpgradeKms / UpgradeApp scripts that upgrade live proxies to the current production source. - script/Manage.s.sol::DeployApp was calling the legacy 5-arg deployAndRegisterApp, silently forcing requireTcbUpToDate=false. Now reads REQUIRE_TCB_UP_TO_DATE env var and uses the 6-arg overload to match master's hardhat-task semantics. Security hardening: - Both contracts switched from OwnableUpgradeable to Ownable2StepUpgradeable. ERC-7201 namespaced storage means no slot collision on upgrade; transferOwnership now stages a pending owner who must acceptOwnership, eliminating the typo-bricks-contract risk. - registerApp's permissionless-by-design intent documented inline in natspec (any non-zero address can be registered by anyone; the downstream allowedOsImages whitelist + delegated isAppAllowed gate authorization). - Slither static analysis configured in slither.config.json with per-line suppression comments + justifications on the four noise-detector hits (factory reentrancy-benign, unused-return on the named-return forward pattern, two unindexed-event-address for backward-compatible log indexers). Baseline: 0 findings. - Inherited Prek hooks (trailing-whitespace, end-of-file-fixer, shellcheck) cleaned up across the anvil helper scripts that came in with the original migration. Verification: forge fmt --check, forge build, forge test --ffi (46/46), slither (0 findings), npx jest (4/4 server tests), npx tsc --noEmit all clean.
1 parent f67b4f6 commit 296be78

124 files changed

Lines changed: 6584 additions & 21965 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docker-build-check.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ jobs:
6363
runs-on: ubuntu-latest
6464
steps:
6565
- uses: actions/checkout@v4
66+
with:
67+
submodules: recursive
6668

6769
- name: Set up Docker Buildx
6870
uses: docker/setup-buildx-action@v3
@@ -103,11 +105,13 @@ jobs:
103105
build/shared/verify-pinned-packages.sh kms-builder-check:latest \
104106
kms/dstack-app/builder/shared/builder-pinned-packages.txt
105107
108+
- name: Install Foundry
109+
uses: foundry-rs/foundry-toolchain@v1
110+
106111
- name: Build KMS contracts
107112
run: |
108113
cd kms/auth-eth
109-
npm ci
110-
npx hardhat compile
114+
forge build
111115
112116
verifier:
113117
runs-on: ubuntu-latest

.github/workflows/foundry-test.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: KMS Auth-ETH Foundry Tests
6+
7+
on:
8+
push:
9+
paths:
10+
- 'kms/auth-eth/**'
11+
- '.github/workflows/foundry-test.yml'
12+
pull_request:
13+
paths:
14+
- 'kms/auth-eth/**'
15+
- '.github/workflows/foundry-test.yml'
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
21+
env:
22+
FOUNDRY_PROFILE: ci
23+
24+
jobs:
25+
check:
26+
name: Foundry project
27+
runs-on: ubuntu-latest
28+
defaults:
29+
run:
30+
working-directory: kms/auth-eth
31+
steps:
32+
- uses: actions/checkout@v4
33+
with:
34+
submodules: recursive
35+
36+
- name: Install Foundry
37+
uses: foundry-rs/foundry-toolchain@v1
38+
39+
- name: Show Forge version
40+
run: |
41+
forge --version
42+
43+
- name: Run Forge fmt
44+
run: |
45+
forge fmt --check
46+
id: fmt
47+
48+
- name: Run Forge build
49+
run: |
50+
forge build --sizes
51+
id: build
52+
53+
- name: Run Forge tests
54+
run: |
55+
forge test --ffi -vvv
56+
id: test

.github/workflows/kms-release.yml

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,22 @@ jobs:
6868
subject-digest: ${{ steps.build-and-push.outputs.digest }}
6969
push-to-registry: true
7070

71-
- name: Setup Node.js
72-
uses: actions/setup-node@v4
73-
with:
74-
node-version: '18'
75-
cache: 'npm'
76-
cache-dependency-path: kms/auth-eth/package-lock.json
71+
- name: Install Foundry
72+
uses: foundry-rs/foundry-toolchain@v1
7773

78-
- name: Install dependencies and compile contracts
74+
- name: Compile contracts with Foundry
7975
run: |
8076
cd kms/auth-eth
81-
npm ci
82-
npx hardhat compile
77+
forge install
78+
forge build
8379
8480
- name: GitHub Release
8581
uses: softprops/action-gh-release@v1
8682
with:
8783
name: "KMS Release v${{ env.VERSION }}"
8884
files: |
89-
kms/auth-eth/artifacts/contracts/DstackKms.sol/DstackKms.json
90-
kms/auth-eth/artifacts/contracts/DstackApp.sol/DstackApp.json
85+
kms/auth-eth/out/DstackKms.sol/DstackKms.json
86+
kms/auth-eth/out/DstackApp.sol/DstackApp.json
9187
body: |
9288
## Docker Image Information
9389

.gitmodules

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
[submodule "kms/auth-eth/lib/forge-std"]
6+
path = kms/auth-eth/lib/forge-std
7+
url = https://github.com/foundry-rs/forge-std
8+
[submodule "kms/auth-eth/lib/openzeppelin-contracts-upgradeable"]
9+
path = kms/auth-eth/lib/openzeppelin-contracts-upgradeable
10+
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
11+
[submodule "kms/auth-eth/lib/openzeppelin-foundry-upgrades"]
12+
path = kms/auth-eth/lib/openzeppelin-foundry-upgrades
13+
url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades

CLAUDE.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,20 @@ cargo clippy -- -D warnings --allow unused_variables
6464

6565
```bash
6666
cd kms/auth-eth
67-
npm install
68-
npm run build # Compile TypeScript
69-
npm test # Run tests
70-
npm run test:coverage # Run tests with coverage
71-
72-
# Hardhat commands
73-
npx hardhat compile
74-
npx hardhat test
75-
npx hardhat node # Start local node
67+
npm install # Install Node.js dependencies for bootAuth server
68+
forge install # Install Foundry dependencies (submodules)
69+
70+
# Build
71+
forge build # Compile smart contracts
72+
npm run build # Build TypeScript server
73+
74+
# Test
75+
forge test --ffi # Run Foundry contract tests
76+
npm test # Run TypeScript server tests
77+
npm run test:coverage # Run TypeScript tests with coverage
78+
79+
# Local development
80+
anvil # Start local Ethereum node
7681
```
7782

7883
### Python SDK
@@ -175,8 +180,8 @@ This rule is enforced in `.cursorrules`.
175180
- Via Web UI: `http://localhost:9080` (or configured port)
176181
- Via CLI: `./vmm-cli.py` (see `docs/vmm-cli-user-guide.md`)
177182
- Requires:
178-
1. On-chain app registration (`npx hardhat kms:create-app`)
179-
2. Adding compose hash to whitelist (`npx hardhat app:add-hash`)
183+
1. On-chain app registration (see `docs/onchain-governance.md`)
184+
2. Adding compose hash to whitelist
180185
3. Deploying via VMM with App ID
181186

182187
### Accessing Deployed Apps

docs/deployment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ Continue? [y/N]
363363

364364
**Before pressing 'y'**, add the compose hash to your auth server whitelist:
365365
- For auth-simple: Add to `composeHashes` array in `auth-config.json`
366-
- For auth-eth: Use `app:add-hash` (see [On-Chain Governance](./onchain-governance.md#register-gateway-app))
366+
- For auth-eth: Use Foundry scripts (see [On-Chain Governance](./onchain-governance.md#register-gateway-app))
367367

368368
Then return to the first terminal and press 'y' to deploy.
369369

docs/onchain-governance.md

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,38 @@ On-chain governance adds:
1313

1414
- Production dstack deployment with KMS and Gateway as CVMs (see [Deployment Guide](./deployment.md))
1515
- Ethereum wallet with funds on Sepolia testnet (or your target network)
16-
- Node.js and npm installed
17-
- Alchemy API key (for Sepolia) - get one at https://www.alchemy.com/
16+
- [Foundry](https://book.getfoundry.sh/getting-started/installation) installed
17+
- Node.js and npm installed (for the bootAuth server)
1818

1919
## Deploy DstackKms Contract
2020

2121
```bash
2222
cd dstack/kms/auth-eth
23-
npm install
24-
npx hardhat compile
25-
PRIVATE_KEY=<your-key> ALCHEMY_API_KEY=<your-key> npx hardhat kms:deploy --with-app-impl --network sepolia
23+
npm install # Install Node.js dependencies
24+
forge install # Install Foundry dependencies
25+
26+
# Deploy contracts (deploys both DstackApp implementation and DstackKms proxy)
27+
PRIVATE_KEY=<your-key> forge script script/Deploy.s.sol:DeployScript \
28+
--broadcast --rpc-url https://eth-sepolia.g.alchemy.com/v2/<your-alchemy-key>
2629
```
2730

28-
The command will prompt for confirmation. Sample output:
31+
Sample output:
2932

3033
```
31-
✅ DstackApp implementation deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
32-
DstackKms Proxy deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
33-
Implementation deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
34+
Deploying with account: 0x...
35+
DstackApp implementation deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
36+
DstackKms implementation deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
37+
DstackKms proxy deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
3438
```
3539

3640
Note the proxy address (e.g., `0x9fE4...`).
3741

3842
Set environment variables for subsequent commands:
3943

4044
```bash
41-
export KMS_CONTRACT_ADDRESS="<DstackKms-proxy-address>"
45+
export KMS_CONTRACT_ADDR="<DstackKms-proxy-address>"
4246
export PRIVATE_KEY="<your-private-key>"
43-
export ALCHEMY_API_KEY="<your-alchemy-key>"
47+
export RPC_URL="https://eth-sepolia.g.alchemy.com/v2/<your-alchemy-key>"
4448
```
4549

4650
## Configure KMS for On-Chain Auth
@@ -52,42 +56,45 @@ KMS_CONTRACT_ADDR=<your-dstack-kms-contract-address>
5256
ETH_RPC_URL=<ethereum-rpc-endpoint>
5357
```
5458

55-
Note: The auth-api uses `KMS_CONTRACT_ADDR`, while Hardhat tasks use `KMS_CONTRACT_ADDRESS`.
56-
5759
The auth-api validates boot requests against the smart contract. See [Deployment Guide](./deployment.md#2-deploy-kms-as-cvm) for complete setup instructions.
5860

5961
## Whitelist OS Image
6062

6163
```bash
62-
npx hardhat kms:add-image --network sepolia 0x<os-image-hash>
64+
OS_IMAGE_HASH=0x<os-image-hash> \
65+
forge script script/Manage.s.sol:AddOsImage --broadcast --rpc-url $RPC_URL
6366
```
6467

65-
Output: `Image added successfully`
68+
Output: `Added OS image hash: 0x...`
6669

6770
The `os_image_hash` is in the `digest.txt` file from the guest OS image build (see [Building Guest Images](./deployment.md#building-guest-images)).
6871

6972
## Register Gateway App
7073

7174
```bash
72-
npx hardhat kms:create-app --network sepolia --allow-any-device
75+
# Create a new app with allowAnyDevice=true
76+
ALLOW_ANY_DEVICE=true \
77+
forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URL
7378
```
7479

7580
Sample output:
7681

7782
```
78-
✅ App deployed and registered successfully!
79-
Proxy Address (App Id): 0x75537828f2ce51be7289709686A69CbFDbB714F1
83+
Deployed new app at: 0x75537828f2ce51be7289709686A69CbFDbB714F1
84+
Owner: 0x...
85+
Allow any device: true
8086
```
8187

82-
Note the App ID (Proxy Address) from the output.
88+
Note the App ID (deployed app address) from the output.
8389

8490
Set it as the gateway app:
8591

8692
```bash
87-
npx hardhat kms:set-gateway --network sepolia <app-id>
93+
GATEWAY_APP_ID=<app-id> \
94+
forge script script/Manage.s.sol:SetGatewayAppId --broadcast --rpc-url $RPC_URL
8895
```
8996

90-
Output: `Gateway App ID set successfully`
97+
Output: `Set gateway app ID: <app-id>`
9198

9299
Add the gateway's compose hash to the whitelist. To compute the compose hash:
93100

@@ -98,10 +105,11 @@ sha256sum /path/to/gateway-compose.json | awk '{print "0x"$1}'
98105
Then add it:
99106

100107
```bash
101-
npx hardhat app:add-hash --network sepolia --app-id <app-id> <compose-hash>
108+
APP_CONTRACT_ADDR=<app-id> COMPOSE_HASH=<compose-hash> \
109+
forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URL
102110
```
103111

104-
Output: `Compose hash added successfully`
112+
Output: `Added compose hash: 0x...`
105113

106114
## Register Apps On-Chain
107115

@@ -110,7 +118,8 @@ For each app you want to deploy:
110118
### Create App
111119

112120
```bash
113-
npx hardhat kms:create-app --network sepolia --allow-any-device
121+
ALLOW_ANY_DEVICE=true \
122+
forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URL
114123
```
115124

116125
Note the App ID from the output.
@@ -126,7 +135,8 @@ sha256sum /path/to/your-app-compose.json | awk '{print "0x"$1}'
126135
Then add it:
127136

128137
```bash
129-
npx hardhat app:add-hash --network sepolia --app-id <app-id> <compose-hash>
138+
APP_CONTRACT_ADDR=<app-id> COMPOSE_HASH=<compose-hash> \
139+
forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URL
130140
```
131141

132142
### Deploy via VMM

kms/auth-eth/.env.example

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Example environment configuration for local testing
6+
7+
# Server configuration
8+
PORT=8000
9+
HOST=127.0.0.1
10+
11+
# Ethereum configuration
12+
ETH_RPC_URL=http://127.0.0.1:8545
13+
KMS_CONTRACT_ADDR=0x0000000000000000000000000000000000000000
14+
15+
# For testing with local Anvil node (Foundry):
16+
# ETH_RPC_URL=http://127.0.0.1:8545
17+
# KMS_CONTRACT_ADDR=<deployed_contract_address>
18+
19+
# For testing with testnet:
20+
# ETH_RPC_URL=https://rpc.sepolia.org
21+
# KMS_CONTRACT_ADDR=<your_deployed_contract_address>

kms/auth-eth/.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
/artifacts
2+
/broadcast
23
/cache
4+
/coverage
35
/dist
4-
/out
6+
/out
7+
8+
# Test logs
9+
anvil-test.log
10+
anvil.log
11+
deploy.log
12+
server-test.log
13+
.env.test

0 commit comments

Comments
 (0)