forked from taikoxyz/taiko-mono
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add automata proving contracts
Signed-off-by: dikel <[email protected]>
- Loading branch information
Showing
3 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
packages/protocol/contracts/verifiers/AttestationVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.24; | ||
|
||
import { IAttestation } from "./IAttestation.sol"; | ||
|
||
contract AttestationVerifier { | ||
IAttestation public attestationVerifier; | ||
|
||
constructor(address _attestationVerifierAddr) { | ||
attestationVerifier = IAttestation(_attestationVerifierAddr); | ||
} | ||
|
||
error INVALID_REPORT(); | ||
error INVALID_REPORT_DATA(); | ||
error REPORT_DATA_MISMATCH(); | ||
|
||
function verifyAttestation(bytes calldata _report, bytes32 _userData) public { | ||
if (address(attestationVerifier) == address(0)) return; | ||
|
||
(bool succ, bytes memory output) = attestationVerifier.verifyAndAttestOnChain(_report); | ||
if (!succ) revert INVALID_REPORT(); | ||
|
||
if (output.length < 32) revert INVALID_REPORT_DATA(); | ||
|
||
bytes32 quoteBodyLast32; | ||
assembly { | ||
quoteBodyLast32 := mload(add(add(output, 0x20), sub(mload(output), 32))) | ||
} | ||
|
||
if (quoteBodyLast32 != _userData) revert REPORT_DATA_MISMATCH(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
/** | ||
* @title Interface standard that implement attestation contracts whose verification logic can be | ||
* implemented | ||
* both on-chain and with Risc0 ZK proofs | ||
* @notice The interface simply provides two verification methods for a given attestation input. | ||
* The user can either pay a possibly hefty gas cost to fully verify an attestation fully on-chain | ||
* OR | ||
* Provides ZK proofs from executing an off-chain program where the verification of such attestation | ||
* is conducted. | ||
* @dev should also implement Risc0 Guest Program to use this interface. | ||
* See https://dev.risczero.com/api/blockchain-integration/bonsai-on-eth to learn more | ||
*/ | ||
interface IAttestation { | ||
/** | ||
* @notice full on-chain verification for an attestation | ||
* @dev must further specify the structure of inputs/outputs, to be serialized and passed to | ||
* this method | ||
* @param input - serialized raw input as defined by the project | ||
* @return success - whether the quote has been successfully verified or not | ||
* @return output - the output upon completion of verification. The output data may require | ||
* post-processing by the consumer. | ||
* For verification failures, the output is simply a UTF-8 encoded string, describing the reason | ||
* for failure. | ||
* @dev can directly type cast the failed output as a string | ||
*/ | ||
function verifyAndAttestOnChain(bytes calldata input) | ||
external | ||
returns (bool success, bytes memory output); | ||
|
||
/** | ||
* @param journal - The output of the Guest program, this includes: | ||
* - VerifiedOutput struct | ||
* - TcbInfo hash | ||
* - QEID hash | ||
* - RootCA hash | ||
* - TCB Signing CA hash | ||
* - Root CRL hash | ||
* - Platform CRL hash | ||
* - Processor CRL hash | ||
* @param seal - The encoded cryptographic proof (i.e. SNARK). | ||
*/ | ||
function verifyAndAttestWithZKProof( | ||
bytes calldata journal, | ||
bytes calldata seal | ||
) | ||
external | ||
returns (bool success, bytes memory output); | ||
} |
188 changes: 188 additions & 0 deletions
188
packages/protocol/contracts/verifiers/ProverRegistry.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.24; | ||
|
||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import "../automata-attestation/lib/QuoteV3Auth/V3Struct.sol"; | ||
import "../common/EssentialContract.sol"; | ||
import "../common/LibStrings.sol"; | ||
import "../L1/ITaikoL1.sol"; | ||
import "./libs/LibPublicInput.sol"; | ||
import "./AttestationVerifier.sol"; | ||
import "./IVerifier.sol"; | ||
|
||
contract ProverRegistry is EssentialContract, IVerifier { | ||
struct ProverInstance { | ||
address addr; | ||
uint256 validUntil; | ||
uint256 teeType; // 1: IntelTDX | ||
} | ||
|
||
struct ReportData { | ||
address addr; | ||
uint256 teeType; | ||
uint256 referenceBlockNumber; | ||
bytes32 referenceBlockHash; | ||
bytes32 binHash; | ||
} | ||
|
||
error INVALID_BLOCK_NUMBER(); | ||
error BLOCK_NUMBER_OUT_OF_DATE(); | ||
error BLOCK_NUMBER_MISMATCH(); | ||
error REPORT_USED(); | ||
error PROVER_TYPE_MISMATCH(); | ||
error PROVER_INVALID_INSTANCE_ID(uint256); | ||
error PROVER_INVALID_ADDR(address); | ||
error PROVER_ADDR_MISMATCH(address, address); | ||
error PROVER_OUT_OF_DATE(uint256); | ||
|
||
event InstanceAdded( | ||
uint256 indexed id, address indexed instance, address replaced, uint256 validUntil | ||
); | ||
|
||
AttestationVerifier public verifier; | ||
uint256 public attestValiditySeconds; | ||
uint256 public maxBlockNumberDiff; | ||
uint256 public chainID; | ||
uint256 public nextInstanceId = 0; | ||
|
||
mapping(bytes32 reportHash => bool used) public attestedReports; | ||
mapping(uint256 proverInstanceID => ProverInstance instance) public attestedProvers; | ||
|
||
uint256[43] private __gap; | ||
|
||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
function initialize( | ||
address _initialOwner, | ||
address _verifierAddr, | ||
address _rollupAddressManager, | ||
uint256 _chainID, | ||
uint256 _attestValiditySeconds, | ||
uint256 _maxBlockNumberDiff | ||
) | ||
public | ||
initializer | ||
{ | ||
verifier = AttestationVerifier(_verifierAddr); | ||
chainID = _chainID; | ||
attestValiditySeconds = _attestValiditySeconds; | ||
maxBlockNumberDiff = _maxBlockNumberDiff; | ||
__Essential_init(_initialOwner, _rollupAddressManager); | ||
} | ||
|
||
function reinitialize( | ||
uint8 i, | ||
address _initialOwner, | ||
address _verifierAddr, | ||
uint256 _chainID, | ||
uint256 _attestValiditySeconds, | ||
uint256 _maxBlockNumberDiff | ||
) | ||
public | ||
reinitializer(i) | ||
{ | ||
verifier = AttestationVerifier(_verifierAddr); | ||
chainID = _chainID; | ||
attestValiditySeconds = _attestValiditySeconds; | ||
maxBlockNumberDiff = _maxBlockNumberDiff; | ||
_transferOwnership(_initialOwner); | ||
} | ||
|
||
/// @notice register prover instance with quote | ||
function register(bytes calldata _report, ReportData calldata _data) external { | ||
_checkBlockNumber(_data.referenceBlockNumber, _data.referenceBlockHash); | ||
bytes32 dataHash = keccak256(abi.encode(_data)); | ||
|
||
verifier.verifyAttestation(_report, dataHash); | ||
|
||
bytes32 reportHash = keccak256(_report); | ||
if (attestedReports[reportHash]) revert REPORT_USED(); | ||
attestedReports[reportHash] = true; | ||
|
||
uint256 instanceID = nextInstanceId + 1; | ||
nextInstanceId += 1; | ||
|
||
uint256 validUnitl = block.timestamp + attestValiditySeconds; | ||
attestedProvers[instanceID] = ProverInstance(_data.addr, validUnitl, _data.teeType); | ||
|
||
emit InstanceAdded(instanceID, _data.addr, address(0), validUnitl); | ||
} | ||
|
||
function checkProver( | ||
uint256 _instanceID, | ||
address _proverAddr | ||
) | ||
public | ||
view | ||
returns (ProverInstance memory) | ||
{ | ||
ProverInstance memory prover; | ||
if (_instanceID == 0) revert PROVER_INVALID_INSTANCE_ID(_instanceID); | ||
if (_proverAddr == address(0)) revert PROVER_INVALID_ADDR(_proverAddr); | ||
prover = attestedProvers[_instanceID]; | ||
if (prover.addr != _proverAddr) revert PROVER_ADDR_MISMATCH(prover.addr, _proverAddr); | ||
if (prover.validUntil < block.timestamp) revert PROVER_OUT_OF_DATE(prover.validUntil); | ||
return prover; | ||
} | ||
|
||
// Due to the inherent unpredictability of blockHash, it mitigates the risk of mass-generation | ||
// of attestation reports in a short time frame, preventing their delayed and gradual | ||
// exploitation. | ||
// This function will make sure the attestation report generated in recent ${maxBlockNumberDiff} | ||
// blocks | ||
function _checkBlockNumber(uint256 blockNumber, bytes32 blockHash) private view { | ||
if (blockNumber >= block.number) revert INVALID_BLOCK_NUMBER(); | ||
if (block.number - blockNumber >= maxBlockNumberDiff) { | ||
revert BLOCK_NUMBER_OUT_OF_DATE(); | ||
} | ||
if (blockhash(blockNumber) != blockHash) revert BLOCK_NUMBER_MISMATCH(); | ||
} | ||
|
||
function verifyProof( | ||
Context calldata _ctx, | ||
TaikoData.Transition calldata _tran, | ||
TaikoData.TierProof calldata _proof | ||
) | ||
external | ||
{ | ||
// Do not run proof verification to contest an existing proof | ||
if (_ctx.isContesting) return; | ||
|
||
// Size is: 89 bytes | ||
// 4 bytes + 20 bytes + 65 bytes (signature) = 89 | ||
// TODO: do we need this check? | ||
// if (_proof.data.length != 89) revert SGX_INVALID_PROOF(); | ||
|
||
uint32 id = uint32(bytes4(_proof.data[:4])); | ||
address newInstance = address(bytes20(_proof.data[4:24])); | ||
|
||
address oldInstance = ECDSA.recover( | ||
LibPublicInput.hashPublicInputs( | ||
_tran, address(this), newInstance, _ctx.prover, _ctx.metaHash, taikoChainId() | ||
), | ||
_proof.data[24:] | ||
); | ||
|
||
ProverInstance memory prover = checkProver(id, oldInstance); | ||
if (_proof.tier != prover.teeType) revert PROVER_TYPE_MISMATCH(); | ||
if (oldInstance != newInstance) { | ||
attestedProvers[id].addr = newInstance; | ||
emit InstanceAdded(id, oldInstance, newInstance, prover.validUntil); | ||
} | ||
} | ||
|
||
/// @inheritdoc IVerifier | ||
function verifyBatchProof( | ||
ContextV2[] calldata, /*_ctxs*/ | ||
TaikoData.TierProof calldata /*_proof*/ | ||
) | ||
external | ||
view | ||
{ } | ||
|
||
function taikoChainId() internal view virtual returns (uint64) { | ||
return ITaikoL1(resolve(LibStrings.B_TAIKO, false)).getConfig().chainId; | ||
} | ||
} |