Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ docs/

# Dotenv file
.env
foundry.lock
13 changes: 0 additions & 13 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,10 @@ import "@eigenlayer-middleware/BLSSignatureChecker.sol";
import "@eigenlayer-middleware/interfaces/IRegistryCoordinator.sol";
import "../src/examples/SimpleVerificationConsumer.sol";

/**
* @title Deploy
* @notice Main deployment script for OpacitySDK contracts
*/
contract Deploy is Script {
// Deployed contracts
BLSSignatureChecker public blsSignatureChecker;
SimpleVerificationConsumer public simpleVerificationConsumer;

/**
* @notice Deploy SimpleVerificationConsumer
* @param blsSignatureCheckerAddress BLS signature checker address
* @param registryCoordinator Registry coordinator address
*/
function run(address blsSignatureCheckerAddress, address registryCoordinator) external {
require(blsSignatureCheckerAddress != address(0), "Invalid BLS address");
require(registryCoordinator != address(0), "Invalid registry coordinator address");
Expand All @@ -44,9 +34,6 @@ contract Deploy is Script {
printDeploymentSummary();
}

/**
* @notice Print deployment summary
*/
function printDeploymentSummary() internal view {
console.log("\n========================================");
console.log(" DEPLOYMENT SUMMARY");
Expand Down
124 changes: 84 additions & 40 deletions src/IOpacitySDK.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import {IBLSSignatureCheckerTypes} from "@eigenlayer-middleware/interfaces/IBLSS

/**
* @title IOpacitySDK
* @notice Interface for OpacitySDK containing all structs, events, and errors
* @notice Interface for the Opacity SDK, providing verifiable private data attestations on-chain
* @dev This interface defines all structs, events, errors, and function signatures for the OpacitySDK.
* The SDK enables verification of BLS-signed commitments from an operator network, allowing
* smart contracts to validate private data attestations without exposing the underlying data.
*/
interface IOpacitySDK {
/**
* @notice Resource tuple (PU, r, PA) representing a resource from a platform
* @param platformUrl Platform URL (e.g., "https://api.bank.com")
* @param resourceName Resource name (e.g., "balance")
* @param param Resource-specific parameter (e.g., "A1")
* @dev Resources are the fundamental building blocks of the Opacity protocol, representing
* data fetched from external platforms. Each resource is uniquely identified by the
* combination of platform URL, resource name, and parameter.
* @param platformUrl The base URL of the platform API (e.g., "https://api.bank.com")
* @param resourceName The name of the resource endpoint (e.g., "balance")
* @param param A resource-specific parameter for the query (e.g., account ID "A1")
*/
struct Resource {
string platformUrl;
Expand All @@ -21,52 +27,67 @@ interface IOpacitySDK {
}

/**
* @notice Public reveal pair (Resource, value)
* @notice Public reveal pair (Resource, value) for publicly committed data
* @dev ValueReveal structures contain data that the user has chosen to reveal publicly
* as part of their attestation. Values are always encoded as strings regardless
* of their underlying type (numbers, booleans, etc.).
* @param resource The resource being revealed
* @param value The primitive value (string, number as string, bool as string, or bytes)
* @param value The revealed value encoded as a string (e.g., "730.25", "true", "Acme Inc")
*/
struct ValueReveal {
Resource resource;
string value;
}

/**
* @notice Composition operation
* @param op Operation type: "sum" or "concat"
* @param resources Array of resources to apply the operation to
* @notice Composition operation for combining multiple resource values
* @dev Compositions allow aggregating multiple resources into a single derived value.
* Supported operations:
* - "sum": Adds numeric values from all resources together
* - "concat": Concatenates string values from all resources
* @param op The operation type to perform ("sum" or "concat")
* @param resources Array of resources whose values will be combined by the operation
*/
struct Composition {
string op;
Resource[] resources;
}

/**
* @notice Conditional atom for conditions
* @param atomType Type of condition: "substr" or "gt"
* @param value The value for the condition (needle for substr, threshold for gt)
* @notice Conditional atom representing a single condition check
* @dev CondAtom structures define individual conditions that can be applied to resources.
* @param atomType The type of condition to evaluate
* @param value The condition parameter
*/
struct CondAtom {
string atomType;
string value;
}

/**
* @notice Condition group
* @param targets Array of resources that must satisfy all conditions
* @param allOf Array of conditional atoms that all targets must satisfy
* @notice Condition group representing a set of conditions applied to target resources
* @dev A ConditionGroup defines that ALL specified conditions (allOf) must be satisfied
* by ALL specified target resources. This enables complex validation logic such as
* "both account balances must be greater than $100" or "employer name must contain 'Inc'".
* @param targets Array of resources that must all satisfy the conditions
* @param allOf Array of conditional atoms that all targets must satisfy (AND logic)
*/
struct ConditionGroup {
Resource[] targets;
CondAtom[] allOf;
}

/**
* @notice Unified Commitment Payload (P) as defined in the schema
* @param userAddr Signer's address
* @param values Optional public reveals as (R, v) pairs
* @param compositions Optional list of composition items
* @param conditions Optional list of condition groups
* @param sig Signature over UID(ProtoTag, UserAddr, P)
* @notice Unified Commitment Payload (P) containing all attestation data
* @dev The CommitmentPayload is the core data structure signed by the operator network.
* It contains the user's address, any publicly revealed values, composition operations,
* and condition groups. The signature field contains the user's signature over the
* unique identifier UID(ProtoTag, UserAddr, P).
* @param userAddr The address of the user making the attestation
* @param values Optional array of publicly revealed (Resource, value) pairs
* @param compositions Optional array of composition operations on resources
* @param conditions Optional array of condition groups that must be satisfied
* @param sig User's signature over the payload identifier
*/
struct CommitmentPayload {
address userAddr;
Expand All @@ -77,11 +98,14 @@ interface IOpacitySDK {
}

/**
* @notice Struct containing all parameters needed for verification
* @param quorumNumbers The quorum numbers to check signatures for
* @param referenceBlockNumber The block number to use as reference for operator set
* @param nonSignerStakesAndSignature The non-signer stakes and signature data computed off-chain
* @param payload The unified commitment payload
* @notice Struct containing all parameters needed for BLS signature verification
* @dev This struct bundles all verification inputs required by the verify() function.
* The referenceBlockNumber must be recent (within BLOCK_STALE_MEASURE blocks) to ensure
* the operator set hasn't changed significantly since the attestation was created.
* @param quorumNumbers The quorum numbers to check signatures against (encoded as bytes)
* @param referenceBlockNumber The block number used to determine the operator set composition
* @param nonSignerStakesAndSignature BLS signature data and non-signer information computed off-chain
* @param payload The unified commitment payload containing the attested data
*/
struct VerificationParams {
bytes quorumNumbers;
Expand All @@ -90,41 +114,61 @@ interface IOpacitySDK {
CommitmentPayload payload;
}

/// @notice Thrown when the BLS signature verification fails
/**
* @notice Thrown when the BLS signature verification fails
* @dev This error indicates that the aggregated BLS signature does not match the expected signers
*/
error InvalidSignature();

/// @notice Thrown when the quorum threshold is not met (signatories own less than required percentage)
/**
* @notice Thrown when the quorum threshold is not met
* @dev Signatories must own at least QUORUM_THRESHOLD percent of the stake for each quorum
*/
error InsufficientQuorumThreshold();

/// @notice Thrown when the reference block number is too old (beyond BLOCK_STALE_MEASURE)
/**
* @notice Thrown when the reference block number is too old
* @dev The reference block must be within BLOCK_STALE_MEASURE blocks of the current block
*/
error StaleBlockNumber();

/// @notice Thrown when the reference block number is in the future
/**
* @notice Thrown when the reference block number is in the future
* @dev The reference block must be less than the current block number
*/
error FutureBlockNumber();

/**
* @notice Compute the payload hash for signature verification
* @dev Implements UID(ProtoTag, UserAddr, P) where P is the commitment payload
* @param payload The commitment payload
* @return The payload hash
* @notice Computes the unique payload hash used for signature verification
* @dev Implements the UID(ProtoTag, UserAddr, P) computation where P is the commitment payload.
* This hash is what the operator network signs to attest to the validity of the payload data.
* @param payload The commitment payload to hash
* @return The keccak256 hash of the encoded payload
*/
function computePayloadHash(CommitmentPayload memory payload) external pure returns (bytes32);

/**
* @notice Function to verify if a signature is valid
* @param params The verification parameters wrapped in a struct
* @return success Whether the verification succeeded
* @notice Verifies a BLS-signed attestation from the operator network
* @dev Checks that:
* 1. The reference block is not in the future
* 2. The reference block is not stale (within BLOCK_STALE_MEASURE blocks)
* 3. The BLS signature is valid
* 4. The signing operators meet the quorum threshold for all specified quorums
* @param params The verification parameters containing quorum info, block reference, and payload
* @return success True if the attestation is valid, reverts otherwise
*/
function verify(VerificationParams calldata params) external view returns (bool success);

/**
* @notice Get the current quorum threshold
* @return The current quorum threshold percentage
* @notice Returns the current quorum threshold percentage
* @dev Signatories must control at least this percentage of stake for verification to succeed
* @return The quorum threshold as a percentage (e.g., 66 means 66%)
*/
function getQuorumThreshold() external view returns (uint8);

/**
* @notice Get the block stale measure
* @notice Returns the maximum age of a reference block in blocks
* @dev Reference blocks older than this value will cause verification to fail with StaleBlockNumber
* @return The number of blocks after which a reference block is considered stale
*/
function getBlockStaleMeasure() external view returns (uint32);
Expand Down
79 changes: 62 additions & 17 deletions src/OpacitySDK.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,66 @@ import {IOpacitySDK} from "./IOpacitySDK.sol";

/**
* @title OpacitySDK
* @notice Lightweight SDK for implementing opacity verification
* @dev Inherit from this contract to add opacity verification capabilities to your contract
* @notice Abstract contract providing BLS signature verification for Opacity attestations
* @dev Inherit from this contract to add Opacity verification capabilities to your contract.
* The SDK integrates with EigenLayer's BLS signature infrastructure to verify that
* a quorum of operators have attested to the validity of private data commitments.
*
* Example usage:
* ```solidity
* contract MyConsumer is OpacitySDK {
* constructor(address _blsChecker) OpacitySDK(_blsChecker) {}
*
* function processVerifiedData(VerificationParams calldata params) external {
* require(this.verify(params), "Verification failed");
* // Process verified data from params.payload
* }
* }
* ```
*/
abstract contract OpacitySDK is IOpacitySDK {
// The BLS signature checker contract
/**
* @notice The BLS signature checker contract used for cryptographic verification
* @dev This contract is provided by EigenLayer and handles the BLS signature math
*/
BLSSignatureChecker public immutable blsSignatureChecker;

// Constants for stake threshold checking
/**
* @notice Denominator used for threshold percentage calculations
* @dev QUORUM_THRESHOLD / THRESHOLD_DENOMINATOR gives the required stake fraction
*/
uint8 public constant THRESHOLD_DENOMINATOR = 100;

/**
* @notice Minimum percentage of stake required for quorum (default: 66%)
* @dev Can be modified by inheriting contracts if needed
*/
uint8 public QUORUM_THRESHOLD = 66;

/**
* @notice Maximum age of reference block in blocks (default: 300 blocks)
* @dev Attestations with older reference blocks will be rejected as stale
*/
uint32 public BLOCK_STALE_MEASURE = 300;

/**
* @notice Constructor for OpacitySDK
* @param _blsSignatureChecker Address of the deployed BLS signature checker contract
* @notice Initializes the OpacitySDK with the required BLS signature checker
* @dev The BLS signature checker must be deployed before this contract and cannot be changed after deployment
* @param _blsSignatureChecker Address of the deployed BLS signature checker contract (must be non-zero)
*/
constructor(address _blsSignatureChecker) {
require(_blsSignatureChecker != address(0), "Invalid BLS signature checker address");
blsSignatureChecker = BLSSignatureChecker(_blsSignatureChecker);
}

/**
* @notice Compute the payload hash for signature verification
* @dev Implements UID(ProtoTag, UserAddr, P) where P is the commitment payload
* @param payload The commitment payload
* @return The payload hash
* @notice Computes the unique payload hash used for signature verification
* @dev Implements the UID(ProtoTag, UserAddr, P) computation where P is the commitment payload.
* The hash is computed over the userAddr, values, compositions, and conditions fields.
* Note: The signature field is intentionally excluded from the hash.
* Note: Protocol tag versioning is prepared but currently disabled.
* @param payload The commitment payload containing the data to hash
* @return The keccak256 hash of the ABI-encoded payload fields
*/
function computePayloadHash(CommitmentPayload memory payload) public pure returns (bytes32) {
// Protocol tag for versioning (commented out for now)
Expand All @@ -47,9 +81,16 @@ abstract contract OpacitySDK is IOpacitySDK {
}

/**
* @notice Function to verify if a signature is valid
* @param params The verification parameters wrapped in a struct
* @return success Whether the verification succeeded
* @notice Verifies a BLS-signed attestation from the Opacity operator network
* @dev Performs the following verification steps:
* 1. Validates the reference block is not in the future (reverts with FutureBlockNumber)
* 2. Validates the reference block is not stale (reverts with StaleBlockNumber)
* 3. Computes the payload hash and verifies BLS signatures via the signature checker
* 4. Checks that each quorum meets the stake threshold (reverts with InsufficientQuorumThreshold)
*
* This function is view-only and does not modify state.
* @param params The verification parameters containing quorum numbers, block reference, signature data, and payload
* @return success Always returns true if verification succeeds; reverts on any failure
*/
function verify(VerificationParams calldata params) external view returns (bool success) {
// Check block number validity
Expand All @@ -65,7 +106,7 @@ abstract contract OpacitySDK is IOpacitySDK {
msgHash, params.quorumNumbers, params.referenceBlockNumber, params.nonSignerStakesAndSignature
);

// Check that signatories own at least 66% of each quorum
// Check that signatories own at least QUORUM_THRESHOLD% of each quorum
for (uint256 i = 0; i < params.quorumNumbers.length; i++) {
require(
stakeTotals.signedStakeForQuorum[i] * THRESHOLD_DENOMINATOR
Expand All @@ -78,15 +119,19 @@ abstract contract OpacitySDK is IOpacitySDK {
}

/**
* @notice Get the current quorum threshold
* @return The current quorum threshold percentage
* @notice Returns the current quorum threshold percentage
* @dev The threshold determines what fraction of stake must sign for verification to succeed.
* For example, 66 means 66% of the total quorum stake must have signed.
* @return The quorum threshold as a percentage (0-100)
*/
function getQuorumThreshold() external view returns (uint8) {
return QUORUM_THRESHOLD;
}

/**
* @notice Get the block stale measure
* @notice Returns the maximum age of a reference block in blocks
* @dev Reference blocks older than current block - BLOCK_STALE_MEASURE will be rejected.
* This prevents replay attacks with old operator sets.
* @return The number of blocks after which a reference block is considered stale
*/
function getBlockStaleMeasure() external view returns (uint32) {
Expand Down
Loading