Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
309 changes: 197 additions & 112 deletions pkg/bindings/ITaskMailbox/binding.go

Large diffs are not rendered by default.

200 changes: 35 additions & 165 deletions pkg/bindings/TaskMailbox/binding.go

Large diffs are not rendered by default.

255 changes: 73 additions & 182 deletions pkg/bindings/TaskMailboxStorage/binding.go

Large diffs are not rendered by default.

53 changes: 43 additions & 10 deletions src/contracts/avs/task/TaskMailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ contract TaskMailbox is
// Calculate the AVS fee using the task hook
uint96 avsFee = taskConfig.taskHook.calculateTaskFee(taskParams);

// Get the operator table reference timestamp
uint32 operatorTableReferenceTimestamp = IBaseCertificateVerifier(_getCertificateVerifier(taskConfig.curveType))
.latestReferenceTimestamp(taskParams.executorOperatorSet);

bytes32 taskHash = keccak256(abi.encode(_globalTaskCount, address(this), block.chainid, taskParams));
_globalTaskCount = _globalTaskCount + 1;

Expand All @@ -147,6 +151,7 @@ contract TaskMailbox is
feeSplit,
TaskStatus.CREATED,
false, // isFeeRefunded
operatorTableReferenceTimestamp,
taskConfig,
taskParams.payload,
bytes(""),
Expand All @@ -168,6 +173,7 @@ contract TaskMailbox is
taskHash,
taskParams.executorOperatorSet.avs,
taskParams.executorOperatorSet.id,
operatorTableReferenceTimestamp,
taskParams.refundCollector,
avsFee,
block.timestamp + taskConfig.taskSLA,
Expand All @@ -194,6 +200,8 @@ contract TaskMailbox is
task.executorOperatorSetTaskConfig.curveType,
task.executorOperatorSetTaskConfig.consensus,
executorOperatorSet,
task.operatorTableReferenceTimestamp,
keccak256(result),
executorCert
);
require(isCertificateValid, CertificateVerificationFailed());
Expand Down Expand Up @@ -320,6 +328,23 @@ contract TaskMailbox is
emit ExecutorOperatorSetRegistered(msg.sender, operatorSet.avs, operatorSet.id, isRegistered);
}

/**
* @notice Gets the certificate verifier for a given curve type
* @param curveType The curve type to get the certificate verifier for
* @return The address of the certificate verifier
*/
function _getCertificateVerifier(
IKeyRegistrarTypes.CurveType curveType
) internal view returns (address) {
if (curveType == IKeyRegistrarTypes.CurveType.BN254) {
return BN254_CERTIFICATE_VERIFIER;
} else if (curveType == IKeyRegistrarTypes.CurveType.ECDSA) {
return ECDSA_CERTIFICATE_VERIFIER;
} else {
revert InvalidCurveType();
}
}

/**
* @notice Validates that the caller is the owner of the operator set
* @param operatorSet The operator set to validate ownership for
Expand All @@ -329,14 +354,7 @@ contract TaskMailbox is
OperatorSet memory operatorSet,
IKeyRegistrarTypes.CurveType curveType
) internal view {
address certificateVerifier;
if (curveType == IKeyRegistrarTypes.CurveType.BN254) {
certificateVerifier = BN254_CERTIFICATE_VERIFIER;
} else if (curveType == IKeyRegistrarTypes.CurveType.ECDSA) {
certificateVerifier = ECDSA_CERTIFICATE_VERIFIER;
} else {
revert InvalidCurveType();
}
address certificateVerifier = _getCertificateVerifier(curveType);

require(
IBaseCertificateVerifier(certificateVerifier).getOperatorSetOwner(operatorSet) == msg.sender,
Expand Down Expand Up @@ -366,13 +384,17 @@ contract TaskMailbox is
* @param curveType The curve type used for signature verification
* @param consensus The consensus configuration
* @param executorOperatorSet The executor operator set
* @param operatorTableReferenceTimestamp The reference timestamp of the operator table
* @param resultHash The hash of the result of the task
* @param executorCert The executor certificate to verify
* @return isCertificateValid Whether the certificate is valid
*/
function _verifyExecutorCertificate(
IKeyRegistrarTypes.CurveType curveType,
Consensus memory consensus,
OperatorSet memory executorOperatorSet,
uint32 operatorTableReferenceTimestamp,
bytes32 resultHash,
bytes memory executorCert
) internal returns (bool isCertificateValid) {
if (consensus.consensusType == ConsensusType.STAKE_PROPORTION_THRESHOLD) {
Expand All @@ -387,7 +409,9 @@ contract TaskMailbox is
IBN254CertificateVerifierTypes.BN254Certificate memory bn254Cert =
abi.decode(executorCert, (IBN254CertificateVerifierTypes.BN254Certificate));

// Validate that the certificate has a non-empty signature
// Validate the certificate
require(bn254Cert.referenceTimestamp == operatorTableReferenceTimestamp, InvalidReferenceTimestamp());
require(bn254Cert.messageHash == resultHash, InvalidMessageHash());
require(bn254Cert.signature.X != 0 && bn254Cert.signature.Y != 0, EmptyCertificateSignature());

isCertificateValid = IBN254CertificateVerifier(BN254_CERTIFICATE_VERIFIER).verifyCertificateProportion(
Expand All @@ -398,7 +422,15 @@ contract TaskMailbox is
IECDSACertificateVerifierTypes.ECDSACertificate memory ecdsaCert =
abi.decode(executorCert, (IECDSACertificateVerifierTypes.ECDSACertificate));

// Validate that the certificate has a non-empty signature
// Validate the certificate
require(ecdsaCert.referenceTimestamp == operatorTableReferenceTimestamp, InvalidReferenceTimestamp());
require(
ecdsaCert.messageHash
== IECDSACertificateVerifier(ECDSA_CERTIFICATE_VERIFIER).calculateCertificateDigest(
ecdsaCert.referenceTimestamp, resultHash
),
InvalidMessageHash()
);
require(ecdsaCert.sig.length > 0, EmptyCertificateSignature());

(isCertificateValid,) = IECDSACertificateVerifier(ECDSA_CERTIFICATE_VERIFIER)
Expand Down Expand Up @@ -439,6 +471,7 @@ contract TaskMailbox is
task.feeSplit,
_getTaskStatus(task),
task.isFeeRefunded,
task.operatorTableReferenceTimestamp,
task.executorOperatorSetTaskConfig,
task.payload,
task.executorCert,
Expand Down
11 changes: 10 additions & 1 deletion src/contracts/interfaces/ITaskMailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@ interface ITaskMailboxTypes {
// Storage slot 2: avs (20 bytes) + avsFee (12 bytes) = 32 bytes
address avs;
uint96 avsFee;
// Storage slot 3: refundCollector (20 bytes) + executorOperatorSetId (4 bytes) + feeSplit (2 bytes) + status (1 byte) + isFeeRefunded (1 byte) = 28 bytes (4 bytes unused)
// Storage slot 3: refundCollector (20 bytes) + executorOperatorSetId (4 bytes) + feeSplit (2 bytes) + status (1 byte) + isFeeRefunded (1 byte) + referenceTimestamp (4 bytes) = 32 bytes
address refundCollector;
uint32 executorOperatorSetId;
uint16 feeSplit;
TaskStatus status;
bool isFeeRefunded;
uint32 operatorTableReferenceTimestamp;
// Dynamic storage slots
ExecutorOperatorSetTaskConfig executorOperatorSetTaskConfig;
bytes payload;
Expand Down Expand Up @@ -162,6 +163,9 @@ interface ITaskMailboxErrors is ITaskMailboxTypes {
/// @notice Thrown when an invalid consensus value is provided
error InvalidConsensusValue();

/// @notice Thrown when a certificate has an invalid reference timestamp
error InvalidReferenceTimestamp();

/// @notice Thrown when a certificate has an empty signature
error EmptyCertificateSignature();

Expand All @@ -176,6 +180,9 @@ interface ITaskMailboxErrors is ITaskMailboxTypes {

/// @notice Thrown when fee split value is invalid (> 10000 bips)
error InvalidFeeSplit();

/// @notice Thrown when a certificate has an invalid message hash
error InvalidMessageHash();
}

/**
Expand Down Expand Up @@ -214,6 +221,7 @@ interface ITaskMailboxEvents is ITaskMailboxTypes {
* @param taskHash Unique identifier of the task
* @param avs Address of the AVS handling the task
* @param executorOperatorSetId ID of the executor operator set
* @param operatorTableReferenceTimestamp Reference timestamp of the operator table
* @param refundCollector Address to receive refunds
* @param avsFee Fee paid to the AVS
* @param taskDeadline Timestamp by which the task must be completed
Expand All @@ -224,6 +232,7 @@ interface ITaskMailboxEvents is ITaskMailboxTypes {
bytes32 indexed taskHash,
address indexed avs,
uint32 executorOperatorSetId,
uint32 operatorTableReferenceTimestamp,
address refundCollector,
uint96 avsFee,
uint256 taskDeadline,
Expand Down
4 changes: 2 additions & 2 deletions src/test/mocks/MockECDSACertificateVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ contract MockECDSACertificateVerifier is IECDSACertificateVerifier {
return bytes32(0);
}

function calculateCertificateDigest(uint32, /*referenceTimestamp*/ bytes32 /*messageHash*/ ) external pure returns (bytes32) {
return bytes32(0);
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) external pure returns (bytes32) {
return keccak256(abi.encode(referenceTimestamp, messageHash));
}

function getTotalStakeWeights(OperatorSet calldata, uint32) external pure returns (uint[] memory) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/mocks/MockECDSACertificateVerifierFailure.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ contract MockECDSACertificateVerifierFailure is IECDSACertificateVerifier {
return bytes32(0);
}

function calculateCertificateDigest(uint32, /*referenceTimestamp*/ bytes32 /*messageHash*/ ) external pure returns (bytes32) {
return bytes32(0);
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) external pure returns (bytes32) {
return keccak256(abi.encode(referenceTimestamp, messageHash));
}

function getTotalStakeWeights(OperatorSet calldata, uint32) external pure returns (uint[] memory) {
Expand Down
Loading