Skip to content

Commit

Permalink
Add natspec to external or public functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
wshino committed Apr 5, 2024
1 parent f711e76 commit 37c0c49
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 3 deletions.
49 changes: 49 additions & 0 deletions packages/contracts/src/EmailAccountRecovery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,48 @@ import "./EmailAuth.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/// @title Email Account Recovery Contract
/// @notice Provides mechanisms for email-based account recovery, leveraging guardians and template-based email verification.
/// @dev This contract is abstract and requires implementation of several methods for handling email authentication and recovery processes.
abstract contract EmailAccountRecovery {
uint8 constant EMAIL_ACCOUNT_RECOVERY_VERSION_ID = 1;
address public verifierAddr;
address public dkimAddr;
address public emailAuthImplementationAddr;

/// @notice Returns the address of the verifier contract.
/// @dev This function is virtual and can be overridden by inheriting contracts.
/// @return The address of the verifier contract.
function verifier() public view virtual returns (address) {
return verifierAddr;
}

/// @notice Returns the address of the DKIM contract.
/// @dev This function is virtual and can be overridden by inheriting contracts.
/// @return The address of the DKIM contract.
function dkim() public view virtual returns (address) {
return dkimAddr;
}

/// @notice Returns the address of the email authentication contract implementation.
/// @dev This function is virtual and can be overridden by inheriting contracts.
/// @return The address of the email authentication contract implementation.
function emailAuthImplementation() public view virtual returns (address) {
return emailAuthImplementationAddr;
}

/// @notice Returns a two-dimensional array of strings representing the subject templates for email acceptance.
/// @dev This function is virtual and should be implemented by inheriting contracts to define specific acceptance subject templates.
/// @return A two-dimensional array of strings, where each inner array represents a set of parameters for a subject template.
function acceptanceSubjectTemplates()
public
view
virtual
returns (string[][] memory);

/// @notice Returns a two-dimensional array of strings representing the subject templates for email recovery.
/// @dev This function is virtual and should be implemented by inheriting contracts to define specific recovery subject templates.
/// @return A two-dimensional array of strings, where each inner array represents a set of parameters for a subject template.
function recoverySubjectTemplates()
public
view
Expand All @@ -49,8 +67,17 @@ abstract contract EmailAccountRecovery {
bytes32 emailNullifier
) internal virtual;

/// @notice Completes the recovery process for an email account.
/// @dev This function must be implemented by inheriting contracts to finalize the recovery process.
function completeRecovery() external virtual;

/// @notice Computes the address for email authentication using the CREATE2 opcode.
/// @dev This function utilizes the `Create2` library to compute the address where an ERC1967Proxy contract
/// will be deployed for email authentication. The computation uses a provided account salt unique to the user's email
/// and the hash of the encoded ERC1967Proxy creation code concatenated with the encoded email authentication implementation
/// address and the initialization call data. This ensures that the computed address is deterministic and unique per user email.
/// @param accountSalt A bytes32 salt value unique to the user's email account, used in the computation of the contract address.
/// @return The computed address where the ERC1967Proxy contract for email authentication will be deployed.
function computeEmailAuthAddress(
bytes32 accountSalt
) public view returns (address) {
Expand All @@ -72,6 +99,11 @@ abstract contract EmailAccountRecovery {
);
}

/// @notice Calculates a unique ID for an email acceptance template using its index.
/// @dev Encodes the email account recovery version ID, "ACCEPTANCE", and the template index,
/// then uses keccak256 to hash these values into a uint256 ID.
/// @param templateIdx The index of the acceptance template.
/// @return The computed uint ID unique to the specified template index.
function computeAcceptanceTemplateId(
uint templateIdx
) public pure returns (uint) {
Expand All @@ -87,6 +119,11 @@ abstract contract EmailAccountRecovery {
);
}

/// @notice Calculates a unique ID for a recovery template based on its index.
/// @dev Encodes the email account recovery version ID, "RECOVERY", and the template index,
/// then uses keccak256 to hash these values into a uint256 ID.
/// @param templateIdx The index of the recovery template.
/// @return Unique uint ID for the given template index.
function computeRecoveryTemplateId(
uint templateIdx
) public pure returns (uint) {
Expand All @@ -102,6 +139,12 @@ abstract contract EmailAccountRecovery {
);
}

/// @notice Handles the acceptance of an email authentication message.
/// @dev This function validates the email authentication message, deploys a new EmailAuth contract as a proxy if validations pass,
/// and sets up the contract with necessary templates and verifiers. It ensures the guardian (computed address) is not deployed,
/// the template ID matches, and the code exists. Finally, it initializes the guardian's EmailAuth contract and records the acceptance.
/// @param emailAuthMsg The email authentication message containing proof and template information.
/// @param templateIdx The index of the template used for acceptance, which is validated against the message's template ID.
function handleAcceptance(
EmailAuthMsg memory emailAuthMsg,
uint templateIdx
Expand Down Expand Up @@ -155,6 +198,12 @@ abstract contract EmailAccountRecovery {
);
}

/// @notice Processes the recovery of an email authentication based on a received message.
/// @dev Validates the provided email authentication message against a deployed guardian address and a specific recovery template.
/// Requires that the guardian is already deployed, and the template ID matches the one in the message. Once validated, it
/// authenticates the email through the guardian's EmailAuth contract and initiates the recovery process with provided parameters.
/// @param emailAuthMsg The email authentication message containing the proof, template ID, and other relevant data for recovery.
/// @param templateIdx The index of the recovery template, used to validate against the message's template ID.
function handleRecovery(
EmailAuthMsg memory emailAuthMsg,
uint templateIdx
Expand Down
46 changes: 45 additions & 1 deletion packages/contracts/src/EmailAuth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ struct EmailAuthMsg {
EmailProof proof;
}

/// @title Email Authentication Contract
/// @notice This contract provides functionalities for email authentication using DKIM and custom verification logic.
/// @dev Inherits from OwnableUpgradeable and UUPSUpgradeable for upgradeability and ownership management.
contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
bytes32 public accountSalt;
ECDSAOwnedDKIMRegistry public dkim;
Expand All @@ -28,7 +31,9 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {

constructor() {}

/// @notice Initialize the contract
/// @notice Initialize the contract with an initial owner and an account salt
/// @param _initialOwner The address of the initial owner
/// @param _accountSalt The account salt for hashing purposes
function initialize(
address _initialOwner,
bytes32 _accountSalt
Expand All @@ -38,14 +43,20 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
timestampCheckEnabled = true;
}

/// @notice Returns the address of the DKIM registry contract
/// @return Address of the DKIM registry contract
function dkimRegistryAddr() public view returns (address) {
return address(dkim);
}

/// @notice Returns the address of the verifier contract
/// @return Address of the verifier contract
function verifierAddr() public view returns (address) {
return address(verifier);
}

/// @notice Updates the address of the DKIM registry contract
/// @param _dkimRegistryAddr The new address of the DKIM registry contract
function updateDKIMRegistry(address _dkimRegistryAddr) public onlyOwner {
require(
_dkimRegistryAddr != address(0),
Expand All @@ -54,11 +65,16 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
dkim = ECDSAOwnedDKIMRegistry(_dkimRegistryAddr);
}

/// @notice Updates the address of the verifier contract
/// @param _verifierAddr The new address of the verifier contract
function updateVerifier(address _verifierAddr) public onlyOwner {
require(_verifierAddr != address(0), "invalid verifier address");
verifier = Verifier(_verifierAddr);
}

/// @notice Retrieves a subject template by its ID
/// @param _templateId The ID of the template to retrieve
/// @return The subject template as an array of strings
function getSubjectTemplate(
uint _templateId
) public view returns (string[] memory) {
Expand All @@ -69,6 +85,9 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
return subjectTemplates[_templateId];
}

/// @notice Inserts a new subject template
/// @param _templateId The ID for the new template
/// @param _subjectTemplate The subject template as an array of strings
function insertSubjectTemplate(
uint _templateId,
string[] memory _subjectTemplate
Expand All @@ -81,6 +100,10 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
subjectTemplates[_templateId] = _subjectTemplate;
}

/// @notice Updates an existing subject template by its ID
/// @dev This function can only be called by the owner of the contract.
/// @param _templateId The ID of the template to update
/// @param _subjectTemplate The new subject template as an array of strings
function updateSubjectTemplate(
uint _templateId,
string[] memory _subjectTemplate
Expand All @@ -93,6 +116,9 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
subjectTemplates[_templateId] = _subjectTemplate;
}

/// @notice Deletes an existing subject template by its ID
/// @dev This function can only be called by the owner of the contract.
/// @param _templateId The ID of the template
function deleteSubjectTemplate(uint _templateId) public onlyOwner {
require(
subjectTemplates[_templateId].length > 0,
Expand All @@ -101,6 +127,13 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
delete subjectTemplates[_templateId];
}

/// @notice Computes the hash of an email authentication message
/// @dev This function takes into account the account salt, whether a code exists, the template ID, and the subject parameters to compute a unique hash for an email authentication message.
/// @param _accountSalt The account salt used for hashing
/// @param _isCodeExist A boolean indicating if a code exists
/// @param _templateId The ID of the email template
/// @param _subjectParams The parameters for the subject of the email
/// @return The computed hash as a bytes32 value
function computeMsgHash(
bytes32 _accountSalt,
bool _isCodeExist,
Expand All @@ -118,6 +151,10 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
);
}

/// @notice Authenticates an email based on the provided email authentication message
/// @dev This function can only be called by the owner of the contract.
/// @param emailAuthMsg The email authentication message containing all necessary information for authentication
/// @return The hash of the authenticated email message
function authEmail(
EmailAuthMsg memory emailAuthMsg
) public onlyOwner returns (bytes32) {
Expand Down Expand Up @@ -181,6 +218,10 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
return msgHash;
}

/// @notice Validates the signature of an authenticated email
/// @param _hash The hash of the email authentication message
/// @param _signature The signature to validate, which is expected to be the email nullifier
/// @return A status code where `0x1626ba7e` indicates a valid signature and `0xffffffff` indicates an invalid signature.
function isValidSignature(
bytes32 _hash,
bytes memory _signature
Expand All @@ -193,6 +234,9 @@ contract EmailAuth is OwnableUpgradeable, UUPSUpgradeable {
}
}

/// @notice Enables or disables the timestamp check for email authentication
/// @dev This function can only be called by the contract owner.
/// @param _enabled Boolean flag to enable or disable the timestamp check
function setTimestampCheckEnabled(bool _enabled) public onlyOwner {
timestampCheckEnabled = _enabled;
}
Expand Down
30 changes: 28 additions & 2 deletions packages/contracts/src/utils/ECDSAOwnedDKIMRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

/// @title ECDSAOwnedDKIMRegistry
/// @notice A DKIM Registry that could be updated by predefined ECDSA signer
/// @title ECDSA Owned DKIM Registry
/// @notice This contract allows for the management of DKIM public key hashes through an ECDSA-signed mechanism. It enables the setting and revoking of DKIM public key hashes for domain names, ensuring that only the authorized signer can perform these operations. The contract leverages an underlying DKIMRegistry contract for the actual storage and validation of public key hashes.
/// @dev The contract uses OpenZeppelin's ECDSA library for signature recovery and the DKIMRegistry for storing the DKIM public key hashes.
contract ECDSAOwnedDKIMRegistry is IDKIMRegistry {
using Strings for *;
using ECDSA for *;
Expand All @@ -18,18 +19,30 @@ contract ECDSAOwnedDKIMRegistry is IDKIMRegistry {
string public constant SET_PREFIX = "SET:";
string public constant REVOKE_PREFIX = "REVOKE:";

/// @notice Initializes the contract with a predefined signer and deploys a new DKIMRegistry.
/// @param _signer The address of the authorized signer who can set or revoke DKIM public key hashes.
constructor(address _signer) {
dkimRegistry = new DKIMRegistry(address(this));
signer = _signer;
}

/// @notice Checks if a DKIM public key hash is valid and not revoked for a given domain name.
/// @param domainName The domain name to check the DKIM public key hash for.
/// @param publicKeyHash The DKIM public key hash to validate.
/// @return bool Returns true if the public key hash is valid and not revoked, false otherwise.
function isDKIMPublicKeyHashValid(
string memory domainName,
bytes32 publicKeyHash
) public view returns (bool) {
return dkimRegistry.isDKIMPublicKeyHashValid(domainName, publicKeyHash);
}

/// @notice Sets a DKIM public key hash for a domain name after validating the provided signature.
/// @param selector The selector associated with the DKIM public key.
/// @param domainName The domain name to set the DKIM public key hash for.
/// @param publicKeyHash The DKIM public key hash to set.
/// @param signature The ECDSA signature proving the operation is authorized by the signer.
/// @dev This function requires that the public key hash is not already set or revoked.
function setDKIMPublicKeyHash(
string memory selector,
string memory domainName,
Expand Down Expand Up @@ -63,6 +76,12 @@ contract ECDSAOwnedDKIMRegistry is IDKIMRegistry {
dkimRegistry.setDKIMPublicKeyHash(domainName, publicKeyHash);
}

/// @notice Revokes a DKIM public key hash for a domain name after validating the provided signature.
/// @param selector The selector associated with the DKIM public key.
/// @param domainName The domain name to revoke the DKIM public key hash for.
/// @param publicKeyHash The DKIM public key hash to revoke.
/// @param signature The ECDSA signature proving the operation is authorized by the signer.
/// @dev This function requires that the public key hash is currently set and not already revoked.
function revokeDKIMPublicKeyHash(
string memory selector,
string memory domainName,
Expand Down Expand Up @@ -96,6 +115,13 @@ contract ECDSAOwnedDKIMRegistry is IDKIMRegistry {
dkimRegistry.revokeDKIMPublicKeyHash(publicKeyHash);
}

/// @notice Computes a signed message string for setting or revoking a DKIM public key hash.
/// @param prefix The operation prefix (SET: or REVOKE:).
/// @param selector The selector associated with the DKIM public key.
/// @param domainName The domain name related to the operation.
/// @param publicKeyHash The DKIM public key hash involved in the operation.
/// @return string The computed signed message.
/// @dev This function is used internally to generate the message that needs to be signed for setting or revoking a public key hash.
function computeSignedMsg(
string memory prefix,
string memory selector,
Expand Down

0 comments on commit 37c0c49

Please sign in to comment.