Skip to content

Commit bb84ddd

Browse files
committed
kms-contract: Add back registered apps
1 parent 821ead7 commit bb84ddd

10 files changed

Lines changed: 204 additions & 54 deletions

File tree

docs/deployment.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,24 +292,26 @@ After the dstack-vmm is ready, you can deploy an app on it following the steps b
292292

293293
### 1. On-chain Registration
294294

295-
In order to deploy an app on the dstack-vmm, you need to deploy an App's control contract AppAuth. Developers can develop their own or choose the reference contract from the Dstack repository. Custom contracts need to implement the IAppAuth interface.
295+
The on-chain registration process includes two steps:
296296

297-
The Dstack repository provides scripts to complete the deployment:
297+
1. Deploy an App's control contract AppAuth. Developers can develop their own or choose the reference contract from the Dstack repository. Custom contracts need to implement the IAppAuth interface.
298+
2. Call KmsAuth.registerApp(appAuthAddress) to register the contract.
298299

299-
**Option 1: Traditional deployment**
300+
The Dstack repository provides scripts to complete these two steps:
301+
302+
**Option 1: Traditional deployment (2 transactions)**
300303
```bash
301304
git clone https://github.com/Dstack-TEE/dstack
302305
cd dstack/kms/auth-eth
303306
npm install
304307
npx hardhat compile
305308
export PRIVATE_KEY=<your eth private key here>
309+
export KMS_CONTRACT_ADDRESS=0xFE6C45aE66344CAEF5E5D7e2cbD476286D651875
306310
npx hardhat app:deploy --allow-any-device --network phala
307311
```
308312

309-
**Option 2: Factory deployment (recommended)**
313+
**Option 2: Factory deployment (1 transaction, recommended)**
310314
```bash
311-
export PRIVATE_KEY=<your eth private key here>
312-
export KMS_CONTRACT_ADDRESS=0xFE6C45aE66344CAEF5E5D7e2cbD476286D651875
313315
npx hardhat kms:create-app --allow-any-device --network phala
314316
```
315317

kms/auth-eth/contracts/KmsAuth.sol

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ contract KmsAuth is
3030
/// @custom:oz-renamed-from tproxyAppId
3131
string public gatewayAppId;
3232

33+
// Mapping of registered apps
34+
mapping(address => bool) public registeredApps;
35+
3336
// Mapping of allowed aggregated MR measurements for running KMS
3437
mapping(bytes32 => bool) public kmsAllowedAggregatedMrs;
3538

@@ -43,6 +46,7 @@ contract KmsAuth is
4346
address public appAuthImplementation;
4447

4548
// Events
49+
event AppRegistered(address appId);
4650
event KmsInfoSet(bytes k256Pubkey);
4751
event KmsAggregatedMrAdded(bytes32 mrAggregated);
4852
event KmsAggregatedMrRemoved(bytes32 mrAggregated);
@@ -117,15 +121,22 @@ contract KmsAuth is
117121
emit GatewayAppIdSet(appId);
118122
}
119123

124+
// Function to register an app
125+
function registerApp(address appId) public {
126+
require(appId != address(0), "Invalid app ID");
127+
registeredApps[appId] = true;
128+
emit AppRegistered(appId);
129+
}
130+
120131
// Function to set AppAuth implementation contract address
121132
function setAppAuthImplementation(address _implementation) external onlyOwner {
122133
require(_implementation != address(0), "Invalid implementation address");
123134
appAuthImplementation = _implementation;
124135
emit AppAuthImplementationSet(_implementation);
125136
}
126137

127-
// Factory method: Deploy AppAuth in single transaction
128-
function deployApp(
138+
// Factory method: Deploy and register AppAuth in single transaction
139+
function deployAndRegisterApp(
129140
address initialOwner,
130141
bool disableUpgrades,
131142
bool allowAnyDevice,
@@ -147,7 +158,8 @@ contract KmsAuth is
147158

148159
// Deploy proxy contract
149160
appId = address(new ERC1967Proxy(appAuthImplementation, initData));
150-
161+
// Register to KMS
162+
registerApp(appId);
151163
emit AppDeployedViaFactory(appId, msg.sender);
152164
}
153165

@@ -221,6 +233,10 @@ contract KmsAuth is
221233
function isAppAllowed(
222234
AppBootInfo calldata bootInfo
223235
) external view override returns (bool isAllowed, string memory reason) {
236+
// Check if app is registered
237+
if (!registeredApps[bootInfo.appId]) {
238+
return (false, "App not registered");
239+
}
224240
// Check if the OS image is allowed
225241
if (!allowedOsImages[bootInfo.osImageHash]) {
226242
return (false, "OS image is not allowed");

kms/auth-eth/foundry-cast-cheatsheet.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,17 @@ cast send $KMS_CONTRACT_ADDRESS "removeKmsDevice(bytes32)" \
181181

182182
```bash
183183
# kms:create-app - Deploy and register AppAuth in single transaction
184-
cast send $KMS_CONTRACT_ADDRESS "deployApp(address,bool,bool,bytes32,bytes32)" \
184+
cast send $KMS_CONTRACT_ADDRESS "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)" \
185185
"$DEPLOYER_ADDRESS" false true \
186186
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" \
187187
"0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321" \
188188
--private-key $PRIVATE_KEY --rpc-url $RPC_URL
189189
# Parameters: (owner, disableUpgrades, allowAnyDevice, initialDeviceId, initialComposeHash)
190190
# Use 0x0000...0000 for empty device/hash values
191-
# To decode return: cast abi-decode "deployApp(address,bool,bool,bytes32,bytes32)(address,address)" RETURN_DATA
191+
# To decode return: cast abi-decode "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)(address,address)" RETURN_DATA
192192

193193
# Example with no initial data:
194-
cast send $KMS_CONTRACT_ADDRESS "deployApp(address,bool,bool,bytes32,bytes32)" \
194+
cast send $KMS_CONTRACT_ADDRESS "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)" \
195195
"$DEPLOYER_ADDRESS" false true \
196196
"0x0000000000000000000000000000000000000000000000000000000000000000" \
197197
"0x0000000000000000000000000000000000000000000000000000000000000000" \
@@ -379,7 +379,7 @@ cast abi-decode "kmsAllowedAggregatedMrs(bytes32)(bool)" RETURN_DATA
379379
cast abi-decode "isAppAllowed((address,bytes32,address,bytes32,bytes32,bytes32,bytes32,string,string[]))(bool,string)" RETURN_DATA
380380

381381
# Decode factory deployment response
382-
cast abi-decode "deployApp(address,bool,bool,bytes32,bytes32)(address,address)" RETURN_DATA
382+
cast abi-decode "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)(address,address)" RETURN_DATA
383383
```
384384

385385
### Get Contract Information
@@ -459,7 +459,7 @@ cast send $KMS_CONTRACT_ADDRESS "addKmsAggregatedMr(bytes32)" "0x..." \
459459
--private-key $PRIVATE_KEY --rpc-url $RPC_URL
460460

461461
# 3. Users can now deploy apps via factory immediately!
462-
cast send $KMS_CONTRACT_ADDRESS "deployApp(address,bool,bool,bytes32,bytes32)" \
462+
cast send $KMS_CONTRACT_ADDRESS "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)" \
463463
"$USER_ADDRESS" false true "0x..." "0x..." \
464464
--private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL
465465
```
@@ -485,7 +485,7 @@ cast send $KMS_CONTRACT_ADDRESS "addKmsAggregatedMr(bytes32)" "0x..." \
485485
--private-key $PRIVATE_KEY --rpc-url $RPC_URL
486486

487487
# 5. Users can now deploy apps via factory
488-
cast send $KMS_CONTRACT_ADDRESS "deployApp(address,bool,bool,bytes32,bytes32)" \
488+
cast send $KMS_CONTRACT_ADDRESS "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)" \
489489
"$USER_ADDRESS" false true "0x..." "0x..." \
490490
--private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL
491491
```
@@ -521,7 +521,7 @@ cast call $KMS_CONTRACT_ADDRESS "owner()" --rpc-url $RPC_URL
521521
| `info:kms` | `cast call ... kmsInfo` | Returns struct |
522522
| `app:deploy` | Complex hardhat task | Multi-transaction deployment |
523523
| `app:deploy-with-data` | Complex hardhat task | Use initializeWithData |
524-
| `app:deploy-factory` | `cast send ... deployApp` | **Single transaction deployment**|
524+
| `app:deploy-factory` | `cast send ... deployAndRegisterApp` | **Single transaction deployment**|
525525
| `kms:set-app-implementation` | `cast send ... setAppAuthImplementation` | Manual setup (rarely needed now) |
526526
| `kms:get-app-implementation` | `cast call ... appAuthImplementation` | Query factory implementation |
527527

@@ -547,7 +547,7 @@ mycast send $APP_AUTH_ADDRESS "addDevice(bytes32)" \
547547
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
548548

549549
# Example: Factory deployment
550-
mycast send $KMS_CONTRACT_ADDRESS "deployApp(address,bool,bool,bytes32,bytes32)" \
550+
mycast send $KMS_CONTRACT_ADDRESS "deployAndRegisterApp(address,bool,bool,bytes32,bytes32)" \
551551
"$DEPLOYER_ADDRESS" false true \
552552
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" \
553553
"0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321"

kms/auth-eth/hardhat.config.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ task("app:deploy", "Deploy AppAuth with a UUPS proxy")
245245
console.log("Deploying with account:", deployerAddress);
246246
console.log("Account balance:", await accountBalance(ethers, deployerAddress));
247247

248+
const kmsContract = await getKmsAuth(ethers);
249+
248250
// Parse device and hash (convert to bytes32, use 0x0 if empty)
249251
const deviceId = taskArgs.device ? taskArgs.device.trim() : "0x0000000000000000000000000000000000000000000000000000000000000000";
250252
const composeHash = taskArgs.hash ? taskArgs.hash.trim() : "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -269,15 +271,54 @@ task("app:deploy", "Deploy AppAuth with a UUPS proxy")
269271
if (!appAuth) {
270272
return;
271273
}
274+
272275
await appAuth.waitForDeployment();
273276
const proxyAddress = await appAuth.getAddress();
274-
console.log("✅ App deployed successfully!");
275-
console.log("AppAuth deployed to (App Id):", proxyAddress);
277+
console.log("AppAuth deployed to:", proxyAddress);
276278

277-
if (hasInitialData) {
278-
const hasDevice = deviceId !== "0x0000000000000000000000000000000000000000000000000000000000000000";
279-
const hasHash = composeHash !== "0x0000000000000000000000000000000000000000000000000000000000000000";
280-
console.log(`Deployed with ${hasDevice ? "1" : "0"} initial device and ${hasHash ? "1" : "0"} initial compose hash`);
279+
const tx = await kmsContract.registerApp(proxyAddress);
280+
const receipt = await waitTx(tx);
281+
282+
// Parse the AppRegistered event from the logs
283+
let appRegisteredEvent = null;
284+
for (const log of receipt.logs) {
285+
try {
286+
const parsedLog = kmsContract.interface.parseLog({
287+
topics: log.topics,
288+
data: log.data
289+
});
290+
291+
if (parsedLog?.name === 'AppRegistered') {
292+
appRegisteredEvent = parsedLog.args;
293+
break;
294+
}
295+
} catch (e) {
296+
continue;
297+
}
298+
}
299+
300+
if (appRegisteredEvent) {
301+
console.log("✅ App deployed and registered successfully!");
302+
console.log("App ID:", appRegisteredEvent.appId);
303+
console.log("Proxy Address:", proxyAddress);
304+
console.log("Owner:", deployerAddress);
305+
console.log("Transaction hash:", tx.hash);
306+
307+
if (hasInitialData) {
308+
const hasDevice = deviceId !== "0x0000000000000000000000000000000000000000000000000000000000000000";
309+
const hasHash = composeHash !== "0x0000000000000000000000000000000000000000000000000000000000000000";
310+
console.log(`Deployed with ${hasDevice ? "1" : "0"} initial device and ${hasHash ? "1" : "0"} initial compose hash`);
311+
}
312+
} else {
313+
console.log("✅ App deployed and registered successfully!");
314+
console.log("Proxy Address:", proxyAddress);
315+
console.log("Transaction hash:", tx.hash);
316+
317+
if (hasInitialData) {
318+
const hasDevice = deviceId !== "0x0000000000000000000000000000000000000000000000000000000000000000";
319+
const hasHash = composeHash !== "0x0000000000000000000000000000000000000000000000000000000000000000";
320+
console.log(`Deployed with ${hasDevice ? "1" : "0"} initial device and ${hasHash ? "1" : "0"} initial compose hash`);
321+
}
281322
}
282323
});
283324

@@ -303,7 +344,7 @@ task("kms:create-app", "Create AppAuth via KMS factory method (single transactio
303344
console.log("Using factory method for single-transaction deployment...");
304345

305346
// Single transaction deployment via factory
306-
const tx = await kmsAuth.deployApp(
347+
const tx = await kmsAuth.deployAndRegisterApp(
307348
deployerAddress, // deployer owns the contract
308349
false, // disableUpgrades
309350
taskArgs.allowAnyDevice,
@@ -315,6 +356,7 @@ task("kms:create-app", "Create AppAuth via KMS factory method (single transactio
315356

316357
// Parse events using contract interface
317358
let factoryEvent = null;
359+
let registeredEvent = null;
318360

319361
for (const log of receipt.logs) {
320362
try {
@@ -325,14 +367,16 @@ task("kms:create-app", "Create AppAuth via KMS factory method (single transactio
325367

326368
if (parsedLog?.name === 'AppDeployedViaFactory') {
327369
factoryEvent = parsedLog.args;
370+
} else if (parsedLog?.name === 'AppRegistered') {
371+
registeredEvent = parsedLog.args;
328372
}
329373
} catch (e) {
330374
// Skip logs that can't be parsed by this contract
331375
continue;
332376
}
333377
}
334378

335-
if (factoryEvent) {
379+
if (factoryEvent && registeredEvent) {
336380
console.log("✅ App deployed and registered successfully!");
337381
console.log("Proxy Address (App Id):", factoryEvent.appId);
338382
console.log("Owner:", factoryEvent.deployer);

kms/auth-eth/test/AppAuth.test.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,6 @@ describe("AppAuth", function () {
8787
expect(isAllowed).to.be.true;
8888
});
8989

90-
it("Should reject invalid app ID", async function () {
91-
const bootInfo = {
92-
tcbStatus: "UpToDate",
93-
advisoryIds: [],
94-
appId: ethers.Wallet.createRandom().address,
95-
composeHash,
96-
instanceId,
97-
deviceId,
98-
mrAggregated,
99-
osImageHash,
100-
mrSystem
101-
};
102-
103-
const [isAllowed, reason] = await appAuth.isAppAllowed(bootInfo);
104-
expect(isAllowed).to.be.false;
105-
expect(reason).to.equal("Wrong app controller");
106-
});
107-
10890
it("Should reject unallowed compose hash", async function () {
10991
const bootInfo = {
11092
tcbStatus: "UpToDate",

kms/auth-eth/test/ethereum.integration.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,8 @@ describe('Integration Tests', () => {
101101
...mockBootInfo,
102102
appId: ethers.Wallet.createRandom().address
103103
};
104-
105104
const result = await backend.checkBoot(badBootInfo, false);
106-
expect(result.reason).to.equal('App not deployed or invalid address');
105+
expect(result.reason).to.equal('App not registered');
107106
expect(result.isAllowed).to.equal(false);
108107
});
109108
});

kms/auth-eth/test/ethereum.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ describe('EthereumBackend', () => {
7272
...mockBootInfo,
7373
appId: ethers.Wallet.createRandom().address
7474
};
75-
7675
const result = await backend.checkBoot(badBootInfo, false);
77-
expect(result.reason).toBe('App not deployed or invalid address');
76+
expect(result.reason).toBe('App not registered');
7877
expect(result.isAllowed).toBe(false);
7978
});
8079
});

kms/auth-eth/test/setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ beforeAll(async () => {
3535
], true) as AppAuth;
3636

3737
const appId = await appAuth.getAddress();
38+
await kmsAuth.registerApp(appId);
3839

3940
// Set up KMS info with the generated app ID
4041
await kmsAuth.setKmsInfo({

0 commit comments

Comments
 (0)