Skip to content

Commit 9c3c6f6

Browse files
committed
write first pair of tests for AllowList.sol
1 parent fe80792 commit 9c3c6f6

File tree

11 files changed

+115
-23
lines changed

11 files changed

+115
-23
lines changed

foundry.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ solc_version = '0.8.28'
55
optimizer_runs = 44444444
66
via_ir = true
77
ffi = true
8-
fs_permissions = [{ access = "read", path = "foundry-out/" }, {access = "read", path = "test/quote.raw"}]
8+
fs_permissions = [{ access = "read", path = "foundry-out/" }, {access = "read", path = "test/raw_tdx_quotes/"}, {access = "read", path = "script/quote.raw/"}]
99
evm_version = "prague"
1010
gas_limit = "3000000000"
1111
fuzz_runs = 10_000

script/AllowList.s.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ contract AllowListScript is Script {
2020

2121
allowlist = new AllowList(ETHEREUM_SEPOLIA_ATTESTATION_FEE_ADDRESS);
2222

23-
// Example: Call registerTEEService with a sample quote
24-
bytes memory sampleQuote = vm.readFileBinary("test/quote.raw");
23+
// Example: Call registerTEEService with a sample quote whose user data is simply `00`
24+
bytes memory sampleQuote = vm.readFileBinary(
25+
"test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin"
26+
);
2527

2628
allowlist.registerTEEService(sampleQuote);
2729

src/AllowList.sol

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,35 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.28;
33

4-
import {AutomataDcapAttestationFee} from "../lib/automata-dcap-attestation/evm/contracts/AutomataDcapAttestationFee.sol";
5-
import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol";
4+
import {IAttestation} from "./interfaces/IAttestation.sol";
65
import {QuoteParser, WorkloadId} from "./utils/QuoteParser.sol";
6+
import {TD10ReportBody} from "automata-dcap-attestation/contracts/types/V4Structs.sol";
7+
8+
// TEE identity and status tracking
9+
struct RegisteredTEE {
10+
uint64 registeredAt; // The most recent timestamp that the TEE last registered with a valid quote
11+
WorkloadId workloadId; // The workloadID of the TEE device
12+
bytes rawQuote; // The raw quote from the TEE device, which is stored to allow for future quote re-verification
13+
}
714

815
/**
916
* @title AllowList
1017
* @dev A contract for managing trusted execution environment (TEE) identities and configurations
1118
* using Automata's Intel DCAP attestation
1219
*/
1320
contract AllowList {
14-
1521
// Constants
1622

1723
// Maximum size for byte arrays to prevent DoS attacks
1824
uint256 public constant MAX_BYTES_SIZE = 20 * 1024; // 20KB limit
1925

2026
// Structs
2127

22-
// TEE identity and status tracking
23-
struct RegisteredTEE {
24-
uint64 registeredAt; // The most recent timestamp that the TEE last registered with a valid quote
25-
WorkloadId workloadId; // The workloadID of the TEE device
26-
bytes rawQuote; // The raw quote from the TEE device, which is stored to allow for future quote re-verification
27-
}
28-
2928
// Storage Variables
3029

3130
// The address of the Automata DCAP Attestation contract, which verifies TEE quotes.
3231
// This is deployed by Automata, and once set on the AllowList, it cannot be changed
33-
address public attestationContract;
32+
IAttestation public attestationContract;
3433

3534
// Used to track registered TEEs by the Ethereum address in the quote that originally registered them
3635
mapping(address => RegisteredTEE) public registeredTEEs;
@@ -54,7 +53,7 @@ contract AllowList {
5453
* @param _attestationContract The address of the attestation contract
5554
*/
5655
constructor(address _attestationContract) {
57-
attestationContract = _attestationContract;
56+
attestationContract = IAttestation(_attestationContract);
5857
}
5958

6059
/**
@@ -73,8 +72,7 @@ contract AllowList {
7372
* @param rawQuote The raw quote from the TEE device. Must be a V4 TDX quote
7473
*/
7574
function registerTEEService(bytes calldata rawQuote) external limitBytesSize(rawQuote) {
76-
(bool success, bytes memory output) =
77-
AutomataDcapAttestationFee(attestationContract).verifyAndAttestOnChain(rawQuote);
75+
(bool success, bytes memory output) = attestationContract.verifyAndAttestOnChain(rawQuote);
7876

7977
if (!success) {
8078
revert InvalidQuote(output);

src/interfaces/IAttestation.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
interface IAttestation {
5+
function verifyAndAttestOnChain(bytes calldata rawQuote) external returns (bool, bytes memory);
6+
}

src/utils/QuoteParser.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ library QuoteParser {
120120
);
121121
}
122122

123-
124123
/**
125124
* Parses and checks that the uint16 version from the quote header is one we accept
126125
* @param rawReportBody The rawQuote bytes generated by Automata's verification logic

test/AllowList.t.sol

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,51 @@
22
pragma solidity 0.8.28;
33

44
import {Test, console} from "forge-std/Test.sol";
5-
import {AllowList} from "../src/AllowList.sol";
5+
import {AllowList, RegisteredTEE} from "../src/AllowList.sol";
6+
import {QuoteParser, WorkloadId} from "../src/utils/QuoteParser.sol";
7+
import {MockAutomataDcapAttestationFee} from "./mocks/MockAutomataDcapAttestationFee.sol";
68

79
contract AllowListTest is Test {
8-
AllowList public registry;
10+
AllowList public allowlist;
11+
MockAutomataDcapAttestationFee public attestationContract;
912

1013
function setUp() public {
11-
registry = new AllowList(address(this));
14+
// deploy a fresh set of test contracts before each test
15+
attestationContract = new MockAutomataDcapAttestationFee();
16+
allowlist = new AllowList(address(attestationContract));
1217
}
1318

14-
function test_registerTEEService() public {}
19+
function test_succesful_registerTEEService() public {
20+
attestationContract.setSuccess(true);
21+
bytes memory output = vm.readFileBinary("test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/output.bin");
22+
attestationContract.setOutput(output);
1523

16-
function testFuzz_registerTEEService(bytes memory _quote) public {}
24+
address expectedAddress = 0xf200f222043C5bC6c70AA6e35f5C5FDe079F3a03;
25+
uint64 expectedRegisteredAt = uint64(block.timestamp);
26+
// note: this is taken directly from the output of QuoteParser.extractWorkloadId, so it's not
27+
// a good test of the QuoteParser.extractWorkloadId function, but it's a good regression test
28+
WorkloadId expectedWorkloadId = WorkloadId.wrap(0x3db2a20cfbe18050a9996d00a94ad82346bc340a438906d04c5a1f960f31a416);
29+
30+
bytes memory quote = vm.readFileBinary("test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin");
31+
vm.expectEmit(address(allowlist));
32+
emit AllowList.TEEServiceRegistered(expectedAddress, expectedWorkloadId, quote, false);
33+
allowlist.registerTEEService(quote);
34+
35+
(uint64 registeredAt, WorkloadId workloadId, bytes memory rawQuote) =
36+
allowlist.registeredTEEs(expectedAddress);
37+
vm.assertEq(registeredAt, expectedRegisteredAt, "Registered at mismatch");
38+
vm.assertEq(WorkloadId.unwrap(workloadId), WorkloadId.unwrap(expectedWorkloadId), "Workload ID mismatch");
39+
vm.assertEq(rawQuote, quote, "Raw quote mismatch");
40+
}
41+
42+
function test_reverts_with_invalid_quote_registerTEEService() public {
43+
attestationContract.setSuccess(false);
44+
// don't bother setting the output, since it should revert before it's used
45+
46+
vm.expectPartialRevert(AllowList.InvalidQuote.selector); // the "partial" just means we don't care about the bytes argument to InvalidQuote(bytes)
47+
bytes memory quote = vm.readFileBinary("test/raw_tdx_quotes/bf42a348f49c9f8ab2ef750ddaffd294c45d8adf947e4d1a72158dcdbd6997c2ca7decaa1ad42648efebdfefe79cbc1b63eb2499fe2374648162fd8f5245f446/quote.bin");
48+
allowlist.registerTEEService(quote);
49+
}
50+
51+
function testFuzz_registerTEEService(bytes memory _quote) public { /** TODO: fuzz things that are fuzzable **/}
1752
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
/**
5+
* @title MockAutomataDcapAttestationFee
6+
* @dev A mock implementation of the AutomataDcapAttestationFee contract for testing
7+
* @dev This mock allows us to control the output of the verifyAndAttestOnChain function,
8+
* and skip dealing with the vast complexity of Automata's DCAP Attestation contract
9+
* @dev This is useful for testing the AllowList contract with different quote and output values
10+
* without having to deploy the AutomataDcapAttestationFee contract
11+
*/
12+
contract MockAutomataDcapAttestationFee {
13+
bool public success;
14+
bytes public output;
15+
16+
constructor() {}
17+
18+
function verifyAndAttestOnChain(bytes calldata /* rawQuote */ ) external view returns (bool, bytes memory) {
19+
return (success, output);
20+
}
21+
22+
function setSuccess(bool _success) public {
23+
success = _success;
24+
}
25+
26+
function setOutput(bytes memory _output) public {
27+
output = _output;
28+
}
29+
}

test/raw_tdx_quotes/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Raw TDX Quotes (For Testing)
2+
3+
This directory contains TDX quote and serialized Output values that we use for testing
4+
5+
## File Structure
6+
7+
Each directory within `raw_tdx_quotes/` is named by the 64-byte hex uncompressed public key that was used for the `TD10ReportBody.reportData` field. Each of these public key directories contains a
8+
9+
- `quote.bin`: which is a TDX quote binary file whose `TD10ReportBody.reportData` is the public key named by its parent directory
10+
- `output.bin`: which is the binary output returned by `AutomataDcapAttestationFee.verifyAndAttestOnChain` when called on the Ethereum Sepolia network
11+
12+
### Helpers
13+
14+
- `hex2bin.py`: a simple python script for writing string hex data (such as `0xdeadbeef`) in its binary form to a file. We use this to take the 0x-prefixed hex string data given to us in events from running Forge scripts and write it in file form that can be read with the Forge cheatcode `vm.readFileBinary`

0 commit comments

Comments
 (0)