Skip to content

Commit

Permalink
feat(protocol): add aggregated sgx verify test (#18160)
Browse files Browse the repository at this point in the history
Co-authored-by: smtmfft <smtmfft@users.noreply.github.com>
Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com>
Co-authored-by: Daniel Wang <dan@taiko.xyz>
Co-authored-by: dantaik <dantaik@users.noreply.github.com>
Co-authored-by: Gavin Yu <gavin@taiko.xyz>
Co-authored-by: YoGhurt111 <YoGhurt111@users.noreply.github.com>
Co-authored-by: David <david@taiko.xyz>
  • Loading branch information
8 people authored Sep 26, 2024
1 parent 868d733 commit 8dda47b
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 16 deletions.
57 changes: 50 additions & 7 deletions packages/protocol/contracts/layer1/verifiers/Risc0Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ contract Risc0Verifier is EssentialContract, IVerifier {
/// @param trusted True if trusted, false otherwise
event ImageTrusted(bytes32 imageId, bool trusted);

error RISC_ZERO_INVALID_IMAGE_ID();
/// @dev Emitted when a proof is verified
event ProofVerified(bytes32 metaHash, bytes32 publicInputHash);

error RISC_ZERO_INVALID_BLOCK_PROOF_IMAGE_ID();
error RISC_ZERO_INVALID_AGGREGATION_IMAGE_ID();
error RISC_ZERO_INVALID_PROOF();

/// @notice Initializes the contract with the provided address manager.
Expand Down Expand Up @@ -59,7 +63,7 @@ contract Risc0Verifier is EssentialContract, IVerifier {
(bytes memory seal, bytes32 imageId) = abi.decode(_proof.data, (bytes, bytes32));

if (!isImageTrusted[imageId]) {
revert RISC_ZERO_INVALID_IMAGE_ID();
revert RISC_ZERO_INVALID_BLOCK_PROOF_IMAGE_ID();
}

bytes32 publicInputHash = LibPublicInput.hashPublicInputs(
Expand All @@ -80,13 +84,52 @@ contract Risc0Verifier is EssentialContract, IVerifier {

/// @inheritdoc IVerifier
function verifyBatchProof(
ContextV2[] calldata, /*_ctxs*/
TaikoData.TierProof calldata /*_proof*/
ContextV2[] calldata _ctxs,
TaikoData.TierProof calldata _proof
)
external
pure
notImplemented
{ }
{
// Decode will throw if not proper length/encoding
(bytes memory seal, bytes32 blockImageId, bytes32 aggregationImageId) =
abi.decode(_proof.data, (bytes, bytes32, bytes32));

// Check if the aggregation program is trusted
if (!isImageTrusted[aggregationImageId]) {
revert RISC_ZERO_INVALID_AGGREGATION_IMAGE_ID();
}
// Check if the block proving program is trusted
if (!isImageTrusted[blockImageId]) {
revert RISC_ZERO_INVALID_BLOCK_PROOF_IMAGE_ID();
}

// Collect public inputs
bytes32[] memory publicInputs = new bytes32[](_ctxs.length + 1);
// First public input is the block proving program key
publicInputs[0] = blockImageId;
// All other inputs are the block program public inputs (a single 32 byte value)
for (uint256 i; i < _ctxs.length; ++i) {
publicInputs[i + 1] = LibPublicInput.hashPublicInputs(
_ctxs[i].tran,
address(this),
address(0),
_ctxs[i].prover,
_ctxs[i].metaHash,
taikoChainId()
);
emit ProofVerified(_ctxs[i].metaHash, publicInputs[i + 1]);
}

// journalDigest is the sha256 hash of the hashed public input
bytes32 journalDigest = sha256(abi.encodePacked(publicInputs));

// call risc0 verifier contract
(bool success,) = resolve(LibStrings.B_RISCZERO_GROTH16_VERIFIER, false).staticcall(
abi.encodeCall(IRiscZeroVerifier.verify, (seal, aggregationImageId, journalDigest))
);
if (!success) {
revert RISC_ZERO_INVALID_PROOF();
}
}

function taikoChainId() internal view virtual returns (uint64) {
return ITaikoL1(resolve(LibStrings.B_TAIKO, false)).getConfig().chainId;
Expand Down
53 changes: 45 additions & 8 deletions packages/protocol/contracts/layer1/verifiers/SgxVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ contract SgxVerifier is EssentialContract, IVerifier {

if (!verified) revert SGX_INVALID_ATTESTATION();

address[] memory _address = new address[](1);
_address[0] = address(bytes20(_attestation.localEnclaveReport.reportData));
address[] memory addresses = new address[](1);
addresses[0] = address(bytes20(_attestation.localEnclaveReport.reportData));

return _addInstances(_address, false)[0];
return _addInstances(addresses, false)[0];
}

/// @inheritdoc IVerifier
Expand Down Expand Up @@ -172,14 +172,51 @@ contract SgxVerifier is EssentialContract, IVerifier {

/// @inheritdoc IVerifier
function verifyBatchProof(
ContextV2[] calldata, /*_ctxs*/
TaikoData.TierProof calldata /*_proof*/
ContextV2[] calldata _ctxs,
TaikoData.TierProof calldata _proof
)
external
view
notImplemented
onlyFromNamedEither(LibStrings.B_TAIKO, LibStrings.B_TIER_TEE_ANY)
{ }
{
// Size is: 109 bytes
// 4 bytes + 20 bytes + 20 bytes + 65 bytes (signature) = 109
if (_proof.data.length != 109) revert SGX_INVALID_PROOF();

uint32 id = uint32(bytes4(_proof.data[:4]));
address oldInstance = address(bytes20(_proof.data[4:24]));
address newInstance = address(bytes20(_proof.data[24:44]));
bytes memory signature = _proof.data[44:];

// Collect public inputs
bytes32[] memory publicInputs = new bytes32[](_ctxs.length + 2);
// First public input is the current instance public key
publicInputs[0] = bytes32(uint256(uint160(oldInstance)));
publicInputs[1] = bytes32(uint256(uint160(newInstance)));
// All other inputs are the block program public inputs (a single 32 byte value)
for (uint256 i; i < _ctxs.length; ++i) {
// TODO: For now this assumes the new instance public key to remain the same
publicInputs[i + 2] = LibPublicInput.hashPublicInputs(
_ctxs[i].tran,
address(this),
newInstance,
_ctxs[i].prover,
_ctxs[i].metaHash,
taikoChainId()
);
}

bytes32 signatureHash = keccak256(abi.encodePacked(publicInputs));
// Verify the blocks
if (oldInstance != ECDSA.recover(signatureHash, signature)) {
revert SGX_INVALID_PROOF();
}

if (!_isInstanceValid(id, oldInstance)) revert SGX_INVALID_INSTANCE();

if (newInstance != oldInstance && newInstance != address(0)) {
_replaceInstance(id, oldInstance, newInstance);
}
}

function taikoChainId() internal view virtual returns (uint64) {
return ITaikoL1(resolve(LibStrings.B_TAIKO, false)).getConfig().chainId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ contract TestRiscZeroVerifier is TaikoL1TestBase {
(IVerifier.Context memory ctx, TaikoData.Transition memory transition) =
_getDummyContextAndTransition();

vm.expectRevert(Risc0Verifier.RISC_ZERO_INVALID_IMAGE_ID.selector);
vm.expectRevert(Risc0Verifier.RISC_ZERO_INVALID_BLOCK_PROOF_IMAGE_ID.selector);
rv.verifyProof(ctx, transition, proof);

vm.stopPrank();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,64 @@ contract RiscZeroGroth16VerifierTest is TaikoL1TestBase {
graffiti: 0x8008500000000000000000000000000000000000000000000000000000000000
});
}

function test_risc0_verifyBatchProof() public {
vm.startPrank(Emma);

bytes32 aggProofImageId = 0x83e7411adcc296e0a021ff032a868434aa2a519b9d11ad44d11d443832280b44;
bytes32 blkProofImageId = 0x28879b90699846864c97f8f32e1b12aabd8ce13135302345d6ad242fa81ab40d;

// proof generation elf
rv.setImageIdTrusted(aggProofImageId, true);
// proof aggregation elf
rv.setImageIdTrusted(blkProofImageId, true);

vm.startPrank(address(L1));

// Context
IVerifier.ContextV2[] memory ctxs = new IVerifier.ContextV2[](2);
ctxs[0] = IVerifier.ContextV2({
metaHash: 0x207b2833fb6d804612da24d8785b870a19c7a3f25fa4aaeb9799cd442d65b031,
blobHash: 0x01354e8725e60ad91b32ec4ab19158572a0a5b06b2d4d83f6269c9a7d068f49b,
prover: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
msgSender: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
blockId: 393_333,
isContesting: false,
blobUsed: true,
tran: TaikoData.Transition({
parentHash: 0xce519622a374dc014c005d7857de26d952751a9067d3e23ffe14da247aa8a399,
blockHash: 0x941d557653da2214cbf3d30af8d9cadbc7b5f77b6c3e48bca548eba04eb9cd79,
stateRoot: 0x4203a2fd98d268d272acb24d91e25055a779b443ff3e732f2cee7abcf639b5e9,
graffiti: 0x8008500000000000000000000000000000000000000000000000000000000000
})
});
ctxs[1] = IVerifier.ContextV2({
metaHash: 0x946ba1a9c02fc2f01da49e31cb5be83c118193d0389987c6be616ce76426b44d,
blobHash: 0x01abac8c1fb54f87ff7b0cbf14259b9d5ee7a8de458c587dd6eda43ef8354b4f,
prover: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
msgSender: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
blockId: 393_334,
isContesting: false,
blobUsed: true,
tran: TaikoData.Transition({
parentHash: 0x941d557653da2214cbf3d30af8d9cadbc7b5f77b6c3e48bca548eba04eb9cd79,
blockHash: 0xc0dad38646ab264be30995b7b7fd02db65e7115126fb52bfad94c0fc9572287c,
stateRoot: 0x222061caab95b6bd0f8dd398088030979efbe56e282cd566f7abd77838558eb9,
graffiti: 0x8008500000000000000000000000000000000000000000000000000000000000
})
});

bytes memory seal =
hex"310fe59810425afc4ed2bae56dfd76e9045f6cd41da30ae8f07a239e86fdb157bf37b0f51b937cb8deccab0d201623d530d0800c208f66dad3f6a38bc1df34408994dec1179209a5f94411e015b20e723512150cfb7e295debeb7ef4f8186cddcf19ba6527ee0d2a0fb8825568682a2fe48e2f73fe9fa052379824751c3bd3f1353f44fe1857e07f5b4801846637b68eafb93aba0c8de8fdfffc76af62a513966f92d9750a977bce0568eb7438fa3497848bfce3e5fd815d9c24b4600e12d0d405d1fd76301ccf27547bddd49a2fa12d1a414f49c2030d0cdf29a87684964a171eefb7e82a5f86acbaacd8cd24d6c3bab06a568f4869087e825ee79237770f23315f3c5c";
// TierProof
TaikoData.TierProof memory proof = TaikoData.TierProof({
tier: 100,
data: abi.encode(seal, blkProofImageId, aggProofImageId)
});

// `verifyProof()`
rv.verifyBatchProof(ctxs, proof);

vm.stopPrank();
}
}
55 changes: 55 additions & 0 deletions packages/protocol/test/layer1/verifiers/SgxVerifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,59 @@ contract TestSgxVerifier is TaikoL1TestBase, AttestationBase {

vm.stopPrank();
}

// Test `verifyBatchProof()` happy path
function test_verifyBatchProofs() public {
// setup instances
address newInstance = address(0x6Aa1108c1903E3AeF092FF46E4C506fD3ac567c0);
address[] memory instances = new address[](1);
instances[0] = newInstance;
uint256[] memory ids = sv.addInstances(instances);
console.log("Instance ID: ", ids[0]);

vm.startPrank(address(L1));

// Context
IVerifier.ContextV2[] memory ctxs = new IVerifier.ContextV2[](2);
ctxs[0] = IVerifier.ContextV2({
metaHash: 0x207b2833fb6d804612da24d8785b870a19c7a3f25fa4aaeb9799cd442d65b031,
blobHash: 0x01354e8725e60ad91b32ec4ab19158572a0a5b06b2d4d83f6269c9a7d068f49b,
prover: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
msgSender: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
blockId: 393_333,
isContesting: false,
blobUsed: true,
tran: TaikoData.Transition({
parentHash: 0xce519622a374dc014c005d7857de26d952751a9067d3e23ffe14da247aa8a399,
blockHash: 0x941d557653da2214cbf3d30af8d9cadbc7b5f77b6c3e48bca548eba04eb9cd79,
stateRoot: 0x4203a2fd98d268d272acb24d91e25055a779b443ff3e732f2cee7abcf639b5e9,
graffiti: 0x8008500000000000000000000000000000000000000000000000000000000000
})
});
ctxs[1] = IVerifier.ContextV2({
metaHash: 0x946ba1a9c02fc2f01da49e31cb5be83c118193d0389987c6be616ce76426b44d,
blobHash: 0x01abac8c1fb54f87ff7b0cbf14259b9d5ee7a8de458c587dd6eda43ef8354b4f,
prover: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
msgSender: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8,
blockId: 393_334,
isContesting: false,
blobUsed: true,
tran: TaikoData.Transition({
parentHash: 0x941d557653da2214cbf3d30af8d9cadbc7b5f77b6c3e48bca548eba04eb9cd79,
blockHash: 0xc0dad38646ab264be30995b7b7fd02db65e7115126fb52bfad94c0fc9572287c,
stateRoot: 0x222061caab95b6bd0f8dd398088030979efbe56e282cd566f7abd77838558eb9,
graffiti: 0x8008500000000000000000000000000000000000000000000000000000000000
})
});

// TierProof
bytes memory data =
hex"000000016aa1108c1903e3aef092ff46e4c506fd3ac567c06aa1108c1903e3aef092ff46e4c506fd3ac567c0dda91ea274c36678a0680bae65216b40bd935e646b6364ea669a6de9b58e0cd11e1c1b86765f98ac5a3113fdc08296aa663378e8e2e44cf08db7a4ba6e5f00f21b";
TaikoData.TierProof memory proof = TaikoData.TierProof({ tier: 0, data: data });

// `verifyProof()`
sv.verifyBatchProof(ctxs, proof);

vm.stopPrank();
}
}

0 comments on commit 8dda47b

Please sign in to comment.