Skip to content

Commit

Permalink
refactor: attestations list
Browse files Browse the repository at this point in the history
  • Loading branch information
geolffreym committed Nov 20, 2024
1 parent 24160b1 commit 7027805
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 63 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"Reentrancy",
"solhint",
"synaps",
"uids",
"UUPS"
]
}
6 changes: 5 additions & 1 deletion contracts/core/interfaces/IAttestationProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ interface IAttestationProvider {
/// @param recipients The addresses of the recipients of the attestation.
/// @param expireAt The timestamp at which the attestation will expire.
/// @param data Additional data associated with the attestation.
function attest(address[] calldata recipients, uint256 expireAt, bytes calldata data) external returns (uint256);
function attest(
address[] calldata recipients,
uint256 expireAt,
bytes calldata data
) external returns (uint256[] memory);

/// @notice Verifies the validity of an attestation for a given attester and recipient.
/// @param attestationId The id of the attestation to verify.
Expand Down
20 changes: 10 additions & 10 deletions contracts/core/interfaces/policies/IPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,7 @@ interface IPolicy {
/// @dev Rights Policies Manager contract should be the only one allowed to call this method.
/// @param holder The rights holder whose authorization is required for accessing the asset.
/// @param agreement An object containing the terms agreed upon between the asset holder and the user.
function enforce(address holder, T.Agreement calldata agreement) external returns (uint256);

/// @notice Retrieves the address of the attestation provider.
/// @return The address of the provider associated with the policy.
function getAttestationProvider() external view returns (address);

/// @notice Retrieves the attestation associated with a specific account and rights holder.
/// @param recipient The address of the account for which the attestation is being retrieved.
/// @param holder The address of the rights holder with whom the agreement was made.
function getAttestation(address recipient, address holder) external view returns (uint256);
function enforce(address holder, T.Agreement calldata agreement) external returns (uint256[] memory);

/// @notice Verifies if a specific account has access rights to a particular asset based on `assetId`.
/// @dev Checks the access policy tied to the provided `assetId` to determine if the account has authorized access.
Expand Down Expand Up @@ -63,4 +54,13 @@ interface IPolicy {
/// @param assetId The unique identifier of the asset for which terms are being resolved.
/// @return A struct containing the terms applicable to the specified content ID.
function resolveTerms(uint256 assetId) external view returns (T.Terms memory);

/// @notice Retrieves the address of the attestation provider.
/// @return The address of the provider associated with the policy.
function getAttestationProvider() external view returns (address);

/// @notice Retrieves the attestation id associated with a specific account and rights holder.
/// @param recipient The address of the account for which the attestation is being retrieved.
/// @param holder The address of the rights holder with whom the agreement was made.
function getAttestation(address recipient, address holder) external view returns (uint256);
}
12 changes: 6 additions & 6 deletions contracts/core/interfaces/rights/IRightsPolicyManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ interface IRightsPolicyManager {
/// @param contentId The identifier of the content to validate the policy status.
function getActivePolicy(address account, uint256 contentId) external view returns (bool, address);

/// @notice Finalizes the agreement by registering the agreed-upon policy, effectively closing the agreement.
/// @param proof The unique identifier of the agreement to be enforced.
/// @param holder The rights holder whose authorization is required for accessing the asset.
/// @param policyAddress The address of the policy contract managing the agreement.
function registerPolicy(uint256 proof, address holder, address policyAddress) external returns (uint256);

/// @notice Verifies if a specific policy is active for the provided account and content.
/// @param account The address of the user whose compliance is being evaluated.
/// @param contentId The identifier of the content to validate the policy status.
/// @param policyAddress The address of the policy contract to check compliance against.
function isActivePolicy(address account, uint256 contentId, address policyAddress) external view returns (bool);

/// @notice Finalizes the agreement by registering the agreed-upon policy, effectively closing the agreement.
/// @param proof The unique identifier of the agreement to be enforced.
/// @param holder The rights holder whose authorization is required for accessing the asset.
/// @param policyAddress The address of the policy contract managing the agreement.
function registerPolicy(uint256 proof, address holder, address policyAddress) external;
}
38 changes: 23 additions & 15 deletions contracts/policies/BasePolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ abstract contract BasePolicy is ReentrancyGuard, IPolicy, ERC165 {
IAssetOwnership public immutable ASSET_OWNERSHIP;

bool private _initialized;
/// @dev attestation registry to store the relation between holder & account
mapping(address => mapping(address => uint256)) public attestations;
/// @dev registry to store the relation between holder & account
mapping(address => mapping(address => uint256)) private _attestations;

/// @notice Emitted when an enforcement process is successfully completed for a given account and holder.
/// @param holder The address of the rights holder managing the asset or access.
/// @param account The address of the user whose access or compliance is being enforced.
/// @param attestationId The unique identifier of the attestation that confirms compliance or access.
event AccessGranted(address indexed holder, address indexed account, uint256 attestationId);
/// @param attestationId The unique identifier of the attestations that confirms compliance or access.
event AttestedAgreement(address indexed holder, address indexed account, uint256 attestationId);

/// @dev Thrown when an attempt is made to access content without proper authorization.
/// This error is used to prevent unauthorized access to content protected by policies or rights.
Expand Down Expand Up @@ -104,11 +104,11 @@ abstract contract BasePolicy is ReentrancyGuard, IPolicy, ERC165 {
return _initialized;
}

/// @notice Retrieves the attestation associated with a specific account and rights holder.
/// @notice Retrieves the attestation id associated with a specific account and rights holder.
/// @param recipient The address of the account for which the attestation is being retrieved.
/// @param holder The address of the rights holder with whom the agreement was made.
function getAttestation(address recipient, address holder) external view returns (uint256) {
return attestations[recipient][holder];
return _attestations[recipient][holder];
}

/// @notice Retrieves the terms associated with a specific rights holder.
Expand Down Expand Up @@ -154,7 +154,7 @@ abstract contract BasePolicy is ReentrancyGuard, IPolicy, ERC165 {
/// @dev The function checks if the provided account complies with the attestation.
/// @param account The address of the user whose access is being verified.
function isCompliant(address account, address holder) public view returns (bool) {
uint256 attestationId = attestations[account][holder];
uint256 attestationId = _attestations[account][holder];
// default uint256 attestation is zero <- means not registered
if (attestationId == 0) return false; // false if not registered
return ATTESTATION_PROVIDER.verify(attestationId, account);
Expand All @@ -170,21 +170,29 @@ abstract contract BasePolicy is ReentrancyGuard, IPolicy, ERC165 {
/// The attestation will be stored on-chain and will have a validity period.
/// @param agreement The agreement structure containing necessary details for the attestation.
/// @param expireAt The timestamp at which the attestation will expire.
function _commit(address holder, T.Agreement memory agreement, uint256 expireAt) internal returns (uint256) {
function _commit(
address holder,
T.Agreement memory agreement,
uint256 expireAt
) internal returns (uint256[] memory) {
bytes memory data = abi.encode(holder, agreement.initiator, address(this), agreement.parties, agreement);
uint256 attestationId = ATTESTATION_PROVIDER.attest(agreement.parties, expireAt, data);
_updateBatchAttestation(holder, attestationId, agreement.parties);
return attestationId;
uint256[] memory attestationIds = ATTESTATION_PROVIDER.attest(agreement.parties, expireAt, data);
_updateBatchAttestation(holder, attestationIds, agreement.parties);
return attestationIds;
}

/// @notice Updates the attestation records for each account.
/// @param attestationId The ID of the attestation.
/// @param attestationIds The ID of the attestations.
/// @param parties The list of account to assign attestation id.
function _updateBatchAttestation(address holder, uint256 attestationId, address[] memory parties) private {
function _updateBatchAttestation(
address holder,
uint256[] memory attestationIds,
address[] memory parties
) private {
uint256 partiesLen = parties.length;
for (uint256 i = 0; i < partiesLen; i = i.uncheckedInc()) {
attestations[parties[i]][holder] = attestationId;
emit AccessGranted(holder, parties[i], attestationId);
_attestations[parties[i]][holder] = attestationIds[i];
emit AttestedAgreement(holder, parties[i], attestationIds[i]);
}
}

Expand Down
5 changes: 1 addition & 4 deletions contracts/policies/access/SubscriptionPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@ contract SubscriptionPolicy is BasePolicy {

// this function should be called only by RM and its used to establish
// any logic or validation needed to set the authorization parameters
// de modo qu en el futuro se pueda usar otro tipo de estructuras como group
function enforce(
address holder,
T.Agreement calldata agreement
) external onlyPolicyManager initialized returns (uint256) {
) external onlyPolicyManager initialized returns (uint256[] memory) {
Package memory pkg = _packages[holder];
if (pkg.pricePerDay == 0) {
// if the holder has not set the package details, can not process the agreement
Expand All @@ -64,8 +63,6 @@ contract SubscriptionPolicy is BasePolicy {
uint256 duration = _verifyDaysFromAmount(paidAmount, pricePerDay, partiesLen);
// subscribe to content owner's catalog (content package)
uint256 subExpire = block.timestamp + (duration * 1 days);
// the agreement is stored in an attestation signed registry
// the recipients is the list of benefitians of the agreement
return _commit(holder, agreement, subExpire);
}

Expand Down
31 changes: 18 additions & 13 deletions contracts/policies/attestation/Eas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ contract EAS is IAttestationProvider {

IEAS public immutable EAS_SERVICE;
bytes32 public immutable SCHEMA_ID;
mapping(address => mapping(uint256 => bytes32)) public attestations;

constructor(address easAddress, bytes32 schemaId) {
EAS_SERVICE = IEAS(easAddress);
Expand All @@ -36,7 +35,11 @@ contract EAS is IAttestationProvider {
/// @param recipients The addresses of the recipients of the attestation.
/// @param expireAt The timestamp at which the attestation will expire.
/// @param data Additional data associated with the attestation.
function attest(address[] calldata recipients, uint256 expireAt, bytes calldata data) external returns (uint256) {
function attest(
address[] calldata recipients,
uint256 expireAt,
bytes calldata data
) external returns (uint256[] memory) {
uint256 recipientsLen = recipients.length;
AttestationRequestData[] memory requests = new AttestationRequestData[](recipientsLen);

Expand All @@ -57,13 +60,8 @@ contract EAS is IAttestationProvider {
multi[0] = MultiAttestationRequest({ schema: SCHEMA_ID, data: requests });
// https://github.com/ethereum-attestation-service/eas-contracts/blob/master/contracts/EAS.sol
bytes32[] memory uids = EAS_SERVICE.multiAttest(multi);

// calculate one global attestation
uint256 global = uint256(keccak256(abi.encodePacked(uids)));
// associate each uid with global and account
_associateUidsWithGlobal(global, uids, recipients);
// on verify get the uid from global and account
return global;
return _convertBytes32ToUint256(uids);
}

/// @notice Verifies the validity of an attestation for a given attester and recipient.
Expand All @@ -72,22 +70,29 @@ contract EAS is IAttestationProvider {
function verify(uint256 attestationId, address recipient) external view returns (bool) {
// check attestation conditions..
// attestationId here is expected as global
bytes32 uid = attestations[recipient][attestationId];
bytes32 uid = bytes32(attestationId);
Attestation memory a = EAS_SERVICE.getAttestation(uid);
// is the same expected criteria as the registered in attestation?
// is the attestation expired?
// who emmited the attestation?
// who emitted the attestation?
if (a.expirationTime > 0 && block.timestamp > a.expirationTime) return false;
if (a.attester != address(this)) return false;
// check if the recipient is listed
return recipient == a.recipient;
}

function _associateUidsWithGlobal(uint256 global, bytes32[] memory uids, address[] memory addresses) private {
uint256 len = addresses.length;
/// @dev Converts an array of bytes32 UIDs into an array of uint256 representations.
/// Each bytes32 element is cast to a uint256 without altering its bitwise structure.
/// @param uids The array of bytes32 UIDs to be converted.
function _convertBytes32ToUint256(bytes32[] memory uids) private pure returns (uint256[] memory) {
uint256 len = uids.length;
uint256[] memory converted = new uint256[](len);
// uint256[] memory converted = new uint256[](len);
for (uint256 i = 0; i < len; i = i.uncheckedInc()) {
/// each account hold a reference to uid bounded by global
attestations[addresses[i]][global] = uids[i];
converted[i] = uint256(uids[i]);
}

return converted;
}
}
25 changes: 22 additions & 3 deletions contracts/policies/attestation/SignGlobal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ contract SignGlobal is IAttestationProvider {
/// @param recipients The addresses of the recipients of the attestation.
/// @param expireAt The timestamp at which the attestation will expire.
/// @param data Additional data associated with the attestation.
function attest(address[] calldata recipients, uint256 expireAt, bytes calldata data) external returns (uint256) {
function attest(
address[] calldata recipients,
uint256 expireAt,
bytes calldata data
) external returns (uint256[] memory) {
Attestation memory a = Attestation({
schemaId: SCHEMA_ID,
attester: address(this),
Expand All @@ -50,7 +54,8 @@ contract SignGlobal is IAttestationProvider {

// Call the SPI instance to register the attestation in the system
// SPI_INSTANCE.attest() stores the attestation and returns an ID for tracking
return SPI_INSTANCE.attest(a, "", "", "");
uint64 attestationId = SPI_INSTANCE.attest(a, "", "", "");
return _fillRecipientsWithIds(recipients, attestationId);
}

/// @notice Verifies the validity of an attestation for a given attester and recipient.
Expand All @@ -61,7 +66,7 @@ contract SignGlobal is IAttestationProvider {
Attestation memory a = SPI_INSTANCE.getAttestation(uint64(attestationId));
// is the same expected criteria as the registered in attestation?
// is the attestation expired?
// who emmited the attestation?
// who emitted the attestation?
if (a.validUntil > 0 && block.timestamp > a.validUntil) return false;
if (a.attester != address(this)) return false;

Expand All @@ -75,6 +80,20 @@ contract SignGlobal is IAttestationProvider {
return false;
}

/// @dev Fills an array with the same `uid` value for each recipient in the input array.
/// @param recipients An array of recipient addresses.
/// @param uid The unique identifier (UID) to assign to each recipient.
/// @return attestationIds An array of UIDs corresponding to each recipient.
function _fillRecipientsWithIds(address[] memory recipients, uint64 uid) private pure returns (uint256[] memory) {
uint256 len = recipients.length;
uint256[] memory attestationIds = new uint256[](len);
for (uint256 i = 0; i < len; i++) {
attestationIds[i] = uid;
}

return attestationIds;
}

/// @notice Converts an array of addresses to an array of bytes.
/// @param addresses The array of addresses to convert.
/// @return An array of addresses encoded as bytes.
Expand Down
Loading

0 comments on commit 7027805

Please sign in to comment.