Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): risc0 verifier contract #16331

Merged
merged 10 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions packages/protocol/contracts/verifiers/RiscZeroVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../common/EssentialContract.sol";
import "../L1/ITaikoL1.sol";
import "./IVerifier.sol";
import "./libs/LibPublicInputHash.sol";

/// @notice Verifier interface for RISC Zero receipts of execution.
/// https://github.com/risc0/risc0-ethereum/blob/release-0.7/contracts/src/IRiscZeroVerifier.sol
adaki2004 marked this conversation as resolved.
Show resolved Hide resolved
interface IRiscZeroVerifier {
/// @notice Verify that the given seal is a valid RISC Zero proof of execution with the
/// given image ID, post-state digest, and journal digest.
/// @dev This method additionally ensures that the input hash is all-zeros (i.e. no
/// committed input), the exit code is (Halted, 0), and there are no assumptions (i.e. the
/// receipt is unconditional).
/// @param seal The encoded cryptographic proof (i.e. SNARK).
/// @param imageId The identifier for the guest program.
/// @param postStateDigest A hash of the final memory state. Required to run the verifier, but
/// otherwise can be left unconstrained for most use cases.
/// @param journalDigest The SHA-256 digest of the journal bytes.
/// @return true if the receipt passes the verification checks. The return code must be checked.
function verify(
bytes calldata seal,
dantaik marked this conversation as resolved.
Show resolved Hide resolved
bytes32 imageId,
bytes32 postStateDigest,
bytes32 journalDigest
)
external
view
returns (bool);
}

/// @title RiscZeroVerifier
/// @custom:security-contact security@taiko.xyz
contract RiscZeroVerifier is EssentialContract, IVerifier {
/// @notice RISC Zero verifier contract address.
IRiscZeroVerifier public riscZeroVerifier;
adaki2004 marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Trusted imageId mapping
mapping(bytes32 imageId => bool trusted) public isImageTrusted;

uint256[48] private __gap;

error RISC_ZERO_INVALID_IMAGE_ID();
error RISC_ZERO_INVALID_PROOF();

/// @notice Initializes the contract with the provided address manager.
/// @param _addressManager The address of the AddressManager.
/// @param _riscZeroVerifier The address of the risc zero verifier contract.
function init(address _addressManager, address _riscZeroVerifier) external initializer {
__Essential_init(address(0), _addressManager);
adaki2004 marked this conversation as resolved.
Show resolved Hide resolved
riscZeroVerifier = IRiscZeroVerifier(_riscZeroVerifier);
}

/// @notice Sets/unsets an the imageId as trusted entity
/// @param _imageId The id of the image.
/// @param _trusted True if trusted, false otherwise.
function setImageIdTrusted(bytes32 _imageId, bool _trusted) external onlyOwner {
isImageTrusted[_imageId] = _trusted;
dantaik marked this conversation as resolved.
Show resolved Hide resolved
}

/// @inheritdoc IVerifier
function verifyProof(
Context calldata _ctx,
TaikoData.Transition calldata _tran,
TaikoData.TierProof calldata _proof
)
external
view
{
// Do not run proof verification to contest an existing proof
if (_ctx.isContesting) return;
dantaik marked this conversation as resolved.
Show resolved Hide resolved

// Decode will throw if not proper length/encoding
(bytes memory seal, bytes32 imageId, bytes32 postStateDigest) =
abi.decode(_proof.data, (bytes, bytes32, bytes32));

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

uint64 chainId = ITaikoL1(resolve("taiko", false)).getConfig().chainId;
bytes32 hash = LibPublicInputHash.hashPublicInputs(
_tran, address(this), address(0), _ctx.prover, _ctx.metaHash, chainId
);

// journalDigest is the sha256 hash of the hashed public input
bytes32 journalDigest = sha256(bytes.concat(hash));

if (!riscZeroVerifier.verify(seal, imageId, postStateDigest, journalDigest)) {
adaki2004 marked this conversation as resolved.
Show resolved Hide resolved
revert RISC_ZERO_INVALID_PROOF();
}
}
}
39 changes: 8 additions & 31 deletions packages/protocol/contracts/verifiers/SgxVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "../common/EssentialContract.sol";
import "../automata-attestation/interfaces/IAttestation.sol";
import "../automata-attestation/lib/QuoteV3Auth/V3Struct.sol";
import "./IVerifier.sol";
import "./libs/LibPublicInputHash.sol";

/// @title SgxVerifier
/// @notice This contract is the implementation of verifying SGX signature proofs
Expand Down Expand Up @@ -152,44 +153,20 @@ contract SgxVerifier is EssentialContract, IVerifier {

uint32 id = uint32(bytes4(_proof.data[:4]));
address newInstance = address(bytes20(_proof.data[4:24]));

uint64 chainId = ITaikoL1(resolve("taiko", false)).getConfig().chainId;

address oldInstance = ECDSA.recover(
getSignedHash(_tran, newInstance, _ctx.prover, _ctx.metaHash), _proof.data[24:]
LibPublicInputHash.hashPublicInputs(
_tran, address(this), newInstance, _ctx.prover, _ctx.metaHash, chainId
),
_proof.data[24:]
);

if (!_isInstanceValid(id, oldInstance)) revert SGX_INVALID_INSTANCE();
_replaceInstance(id, oldInstance, newInstance);
}

/// @notice Gets the signed hash for the proof verification.
/// @param _tran The transition to verify.
/// @param _newInstance The new instance address.
/// @param _prover The prover address.
/// @param _metaHash The meta hash.
/// @return The signed hash.
function getSignedHash(
TaikoData.Transition memory _tran,
address _newInstance,
address _prover,
bytes32 _metaHash
)
public
view
returns (bytes32)
{
address taikoL1 = resolve("taiko", false);
return keccak256(
abi.encode(
"VERIFY_PROOF",
ITaikoL1(taikoL1).getConfig().chainId,
address(this),
_tran,
_newInstance,
_prover,
_metaHash
)
);
}

function _addInstances(
address[] memory _instances,
bool instantValid
Expand Down
38 changes: 38 additions & 0 deletions packages/protocol/contracts/verifiers/libs/LibPublicInputHash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../../L1/TaikoData.sol";

/// @title LibPublicInputHash
/// @notice A library for handling hashing the so-called public input hash, used by sgx and zk
/// proofs.
/// @custom:security-contact security@taiko.xyz
library LibPublicInputHash {
dantaik marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Hashes the public input for the proof verification.
/// @param _tran The transition to verify.
/// @param _verifierContract The contract address which as current verifier.
/// @param _newInstance The new instance address. For SGX it is the new signer address, for ZK
/// this variable is not used and must have value address(0).
/// @param _prover The prover address.
/// @param _metaHash The meta hash.
/// @param _chainId The chain id.
/// @return The public input hash.
function hashPublicInputs(
TaikoData.Transition memory _tran,
address _verifierContract,
address _newInstance,
address _prover,
bytes32 _metaHash,
uint64 _chainId
)
public
pure
returns (bytes32)
{
return keccak256(
abi.encode(
"VERIFY_PROOF", _chainId, _verifierContract, _tran, _newInstance, _prover, _metaHash
)
);
}
}
6 changes: 5 additions & 1 deletion packages/protocol/test/L1/TaikoL1TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ abstract contract TaikoL1TestBase is TaikoTest {
TaikoL1 public L1;
TaikoData.Config conf;
uint256 internal logCount;
RiscZeroVerifier public rv;
SgxVerifier public sv;
GuardianVerifier public gv;
GuardianProver public gp;
Expand Down Expand Up @@ -330,7 +331,10 @@ abstract contract TaikoL1TestBase is TaikoTest {
view
returns (bytes memory signature)
{
bytes32 digest = sv.getSignedHash(tran, newInstance, prover, metaHash);
uint64 chainId = L1.getConfig().chainId;
bytes32 digest = LibPublicInputHash.hashPublicInputs(
tran, address(sv), newInstance, prover, metaHash, chainId
);

uint256 signerPrivateKey;

Expand Down
2 changes: 2 additions & 0 deletions packages/protocol/test/TaikoTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import "../contracts/tokenvault/ERC1155Vault.sol";

import "../contracts/L1/TaikoToken.sol";
import "../contracts/L1/TaikoL1.sol";
import "../contracts/verifiers/libs/LibPublicInputHash.sol";
import "../contracts/verifiers/SgxVerifier.sol";
import "../contracts/verifiers/RiscZeroVerifier.sol";
import "../contracts/verifiers/GuardianVerifier.sol";
import "../contracts/L1/tiers/TestnetTierProvider.sol";
import "../contracts/L1/tiers/ITierProvider.sol";
Expand Down
Loading
Loading