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
30 changes: 25 additions & 5 deletions script/DeployOpacityExamples.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import "../src/examples/StorageQueryConsumer.sol";
contract DeployOpacityExamples is Script {
// Registry Coordinator address (testnet holesky)
address constant REGISTRY_COORDINATOR = 0x3e43AA225b5cB026C5E8a53f62572b10D526a50B;

// Watchtower address (can be set via environment variable or hardcoded)
address public watchtowerAddress;

// Deployed contract addresses
BLSSignatureChecker public blsSignatureChecker;
Expand All @@ -23,10 +26,20 @@ contract DeployOpacityExamples is Script {

function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// Try to get watchtower address from environment, otherwise use a default
try vm.envAddress("WATCHTOWER_ADDRESS") returns (address _watchtower) {
watchtowerAddress = _watchtower;
} catch {
// Default watchtower address for testing (should be replaced in production)
watchtowerAddress = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
console.log("WARNING: Using default watchtower address. Set WATCHTOWER_ADDRESS env var for production.");
}

console.log("Starting OpacitySDK deployment...");
console.log("Deployer address:", vm.addr(deployerPrivateKey));
console.log("Registry Coordinator:", REGISTRY_COORDINATOR);
console.log("Watchtower address:", watchtowerAddress);

vm.startBroadcast(deployerPrivateKey);

Expand All @@ -37,12 +50,12 @@ contract DeployOpacityExamples is Script {

// Step 2: Deploy Simple Verification Consumer
console.log("\n=== Step 2: Deploying Simple Verification Consumer ===");
simpleVerificationConsumer = new SimpleVerificationConsumer(address(blsSignatureChecker));
simpleVerificationConsumer = new SimpleVerificationConsumer(address(blsSignatureChecker), watchtowerAddress);
console.log("Simple Verification Consumer deployed at:", address(simpleVerificationConsumer));

// Step 3: Deploy Storage Query Consumer
console.log("\n=== Step 3: Deploying Storage Query Consumer ===");
storageQueryConsumer = new StorageQueryConsumer(address(blsSignatureChecker));
storageQueryConsumer = new StorageQueryConsumer(address(blsSignatureChecker), watchtowerAddress);
console.log("Storage Query Consumer deployed at:", address(storageQueryConsumer));

vm.stopBroadcast();
Expand All @@ -59,6 +72,7 @@ contract DeployOpacityExamples is Script {
console.log(" DEPLOYMENT SUMMARY");
console.log("========================================");
console.log("Registry Coordinator: ", REGISTRY_COORDINATOR);
console.log("Watchtower Address: ", watchtowerAddress);
console.log("BLS Signature Checker: ", address(blsSignatureChecker));
console.log("Simple Verification Consumer:", address(simpleVerificationConsumer));
console.log("Storage Query Consumer: ", address(storageQueryConsumer));
Expand All @@ -68,17 +82,23 @@ contract DeployOpacityExamples is Script {
console.log("\n=== Verification Checks ===");
console.log("Simple Consumer BLS Address: ", address(simpleVerificationConsumer.blsSignatureChecker()));
console.log("Storage Consumer BLS Address:", address(storageQueryConsumer.blsSignatureChecker()));
console.log("Simple Consumer Watchtower: ", simpleVerificationConsumer.watchtowerAddress());
console.log("Storage Consumer Watchtower: ", storageQueryConsumer.watchtowerAddress());

bool simpleLinked = address(simpleVerificationConsumer.blsSignatureChecker()) == address(blsSignatureChecker);
bool storageLinked = address(storageQueryConsumer.blsSignatureChecker()) == address(blsSignatureChecker);
bool simpleWatchtowerSet = simpleVerificationConsumer.watchtowerAddress() == watchtowerAddress;
bool storageWatchtowerSet = storageQueryConsumer.watchtowerAddress() == watchtowerAddress;

console.log("Simple Consumer properly linked: ", simpleLinked);
console.log("Storage Consumer properly linked:", storageLinked);
console.log("Simple Consumer watchtower set: ", simpleWatchtowerSet);
console.log("Storage Consumer watchtower set:", storageWatchtowerSet);

if (simpleLinked && storageLinked) {
console.log("All contracts deployed and linked successfully!");
if (simpleLinked && storageLinked && simpleWatchtowerSet && storageWatchtowerSet) {
console.log("\nAll contracts deployed and configured successfully!");
} else {
console.log("Contract linking verification failed!");
console.log("\nWARNING: Contract configuration verification failed!");
}
}
}
83 changes: 80 additions & 3 deletions src/OpacitySDK.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ abstract contract OpacitySDK {
* @param value The value associated with the operation
* @param operatorThreshold The operator threshold value for the operation
* @param signature The signature string
* @param watchtowerSignature The watchtower's ECDSA signature for additional verification
*/
struct VerificationParams {
bytes quorumNumbers;
Expand All @@ -34,37 +35,54 @@ abstract contract OpacitySDK {
string value;
uint256 operatorThreshold;
string signature;
bytes watchtowerSignature;
}

// The BLS signature checker contract
BLSSignatureChecker public immutable blsSignatureChecker;

// Watchtower state variables
address public watchtowerAddress;
bool public watchtowerEnabled;

// Constants for stake threshold checking
uint8 public constant THRESHOLD_DENOMINATOR = 100;
uint8 public QUORUM_THRESHOLD = 1;
uint32 public BLOCK_STALE_MEASURE = 300;

// Events
event WatchtowerUpdated(address indexed oldWatchtower, address indexed newWatchtower);
event WatchtowerStatusChanged(bool enabled);
event WatchtowerVerification(bytes32 indexed msgHash, bool verified);

// Custom errors
error InvalidSignature();
error InsufficientQuorumThreshold();
error StaleBlockNumber();
error FutureBlockNumber();
error WatchtowerSignatureRequired();
error InvalidWatchtowerSignature();
error UnauthorizedWatchtowerUpdate();

/**
* @notice Constructor for OpacitySDK
* @param _blsSignatureChecker Address of the deployed BLS signature checker contract
* @param _watchtowerAddress Address of the watchtower signer
*/
constructor(address _blsSignatureChecker) {
constructor(address _blsSignatureChecker, address _watchtowerAddress) {
require(_blsSignatureChecker != address(0), "Invalid BLS signature checker address");
require(_watchtowerAddress != address(0), "Invalid watchtower address");
blsSignatureChecker = BLSSignatureChecker(_blsSignatureChecker);
watchtowerAddress = _watchtowerAddress;
watchtowerEnabled = true;
}

/**
* @notice Function to verify if a signature is valid
* @param params The verification parameters wrapped in a struct
* @return success Whether the verification succeeded
*/
function verify(VerificationParams calldata params) external view returns (bool success) {
function verify(VerificationParams calldata params) external returns (bool success) {
// Check block number validity
require(params.referenceBlockNumber < block.number, FutureBlockNumber());
require((params.referenceBlockNumber + BLOCK_STALE_MEASURE) >= uint32(block.number), StaleBlockNumber());
Expand All @@ -81,7 +99,18 @@ abstract contract OpacitySDK {
)
);

// Verify the signatures using checkSignatures
// Step 1: Verify watchtower signature if enabled
if (watchtowerEnabled) {
require(params.watchtowerSignature.length > 0, WatchtowerSignatureRequired());

// Verify watchtower signature
bool watchtowerValid = _verifyWatchtowerSignature(msgHash, params.watchtowerSignature);
require(watchtowerValid, InvalidWatchtowerSignature());

emit WatchtowerVerification(msgHash, true);
}

// Step 2: Verify operator quorum (existing logic)
(IBLSSignatureCheckerTypes.QuorumStakeTotals memory stakeTotals,) = blsSignatureChecker.checkSignatures(
msgHash, params.quorumNumbers, params.referenceBlockNumber, params.nonSignerStakesAndSignature
);
Expand All @@ -98,6 +127,54 @@ abstract contract OpacitySDK {
return true;
}

/**
* @notice Internal function to verify watchtower signature
* @param msgHash The message hash to verify
* @param signature The ECDSA signature from watchtower
* @return Whether the signature is valid
*/
function _verifyWatchtowerSignature(bytes32 msgHash, bytes memory signature) internal view returns (bool) {
// Ensure signature is the correct length
require(signature.length == 65, "Invalid signature length");

bytes32 r;
bytes32 s;
uint8 v;

// Extract r, s, v from signature
assembly {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
v := byte(0, mload(add(signature, 96)))
}

// Recover signer address
address signer = ecrecover(msgHash, v, r, s);
return signer == watchtowerAddress;
}

/**
* @notice Update the watchtower address
* @param newWatchtower The new watchtower address
* @dev Can only be called by the contract owner/admin
*/
function updateWatchtower(address newWatchtower) external virtual {
require(newWatchtower != address(0), "Invalid watchtower address");
address oldWatchtower = watchtowerAddress;
watchtowerAddress = newWatchtower;
emit WatchtowerUpdated(oldWatchtower, newWatchtower);
}

/**
* @notice Enable or disable watchtower verification
* @param enabled Whether to enable watchtower verification
* @dev Can only be called by the contract owner/admin
*/
function setWatchtowerStatus(bool enabled) external virtual {
watchtowerEnabled = enabled;
emit WatchtowerStatusChanged(enabled);
}

/**
* @notice Get the current quorum threshold
* @return The current quorum threshold percentage
Expand Down
24 changes: 21 additions & 3 deletions src/examples/SimpleVerificationConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import "../OpacitySDK.sol";
import "@eigenlayer-middleware/interfaces/IBLSSignatureChecker.sol";

contract SimpleVerificationConsumer is OpacitySDK {
event DataVerified(address user, string platform, string resource, string value, bool isValid);
event DataVerified(address user, string platform, string resource, string value, bool isValid, bool watchtowerVerified);

/**
* @notice Constructor for SimpleVerificationConsumer
* @param _blsSignatureChecker Address of the deployed BLS signature checker contract
* @param _watchtowerAddress Address of the watchtower signer
*/
constructor(address _blsSignatureChecker) OpacitySDK(_blsSignatureChecker) {}
constructor(address _blsSignatureChecker, address _watchtowerAddress)
OpacitySDK(_blsSignatureChecker, _watchtowerAddress) {}

/**
* @notice Verify user data using VerificationParams struct
Expand All @@ -21,9 +23,25 @@ contract SimpleVerificationConsumer is OpacitySDK {
function verifyUserData(VerificationParams calldata params) public returns (bool) {
try this.verify(params) returns (bool verified) {
// Verification successful - emit event
emit DataVerified(params.userAddress, params.platform, params.resource, params.value, verified); // derefrence by using the struct params
emit DataVerified(
params.userAddress,
params.platform,
params.resource,
params.value,
verified,
watchtowerEnabled
);
return verified;
} catch {
// Verification failed - emit event with false
emit DataVerified(
params.userAddress,
params.platform,
params.resource,
params.value,
false,
watchtowerEnabled
);
return false;
}
}
Expand Down
17 changes: 11 additions & 6 deletions src/examples/StorageQueryConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ contract StorageQueryConsumer is OpacitySDK {
string verifiedValue;
uint256 timestamp;
bytes32 verificationHash;
bool watchtowerVerified;
}

mapping(address => VerificationResult) public userVerifications;

event DataVerified(address indexed user, string verifiedValue, bytes32 verificationHash, bool success);
event DataVerified(address indexed user, string verifiedValue, bytes32 verificationHash, bool success, bool watchtowerVerified);

/**
* @notice Constructor for StorageQueryConsumer
* @param _blsSignatureChecker Address of the deployed BLS signature checker contract
* @param _watchtowerAddress Address of the watchtower signer
*/
constructor(address _blsSignatureChecker) OpacitySDK(_blsSignatureChecker) {}
constructor(address _blsSignatureChecker, address _watchtowerAddress)
OpacitySDK(_blsSignatureChecker, _watchtowerAddress) {}

/**
* @notice Verify private data using VerificationParams struct
Expand All @@ -48,10 +51,11 @@ contract StorageQueryConsumer is OpacitySDK {
isVerified: verified,
verifiedValue: params.value,
timestamp: block.timestamp,
verificationHash: verificationHash
verificationHash: verificationHash,
watchtowerVerified: watchtowerEnabled
});

emit DataVerified(params.userAddress, params.value, verificationHash, verified); // derefrence by using the struct params
emit DataVerified(params.userAddress, params.value, verificationHash, verified, watchtowerEnabled);
return (verified, params.value);
} catch {
return (false, "");
Expand All @@ -75,14 +79,15 @@ contract StorageQueryConsumer is OpacitySDK {
* @return verifiedValue The verified value
* @return timestamp When the verification was made
* @return verificationHash The hash of the verification
* @return watchtowerVerified Whether watchtower was involved in verification
*/
function getUserVerification(address user)
external
view
returns (bool isValid, string memory verifiedValue, uint256 timestamp, bytes32 verificationHash)
returns (bool isValid, string memory verifiedValue, uint256 timestamp, bytes32 verificationHash, bool watchtowerVerified)
{
VerificationResult memory result = userVerifications[user];
return (result.isVerified, result.verifiedValue, result.timestamp, result.verificationHash);
return (result.isVerified, result.verifiedValue, result.timestamp, result.verificationHash, result.watchtowerVerified);
}

/**
Expand Down
Loading
Loading