Data Index
As outlined in the Core Concepts, the IDataIndex
interface defines functions to manage access control for Data Managers to Data Points, as well as to the data associated with these Data Points within specific Data Objects. This interface extends the IIDManager
interface.
In this section, we will explain the content of the minimalistic implementation of the Data Index.
Following the standard interface, the implementation can be divided into two main parts: methods for managing permissions and methods for forwarding operations. In this specific implementation, an access control mechanism has been enabled, which is why the functionalities have been extended. Additionally, note that IDataIndex
supports Data Index IDs in line with the IDManager
standard interface.
/**
* @title Data Index contract
* @notice Minimalistic implementation of a Data Index contract
*/
contract DataIndex is IDataIndex, AccessControl {
// Implementation
}
For storing and managing the permissions of Data Managers allowed to write to specific Data Points, you’ll find:
/// @dev Mapping of DataPoint to DataManagers allowed to write to this DP (in any DataObject)
mapping(DataPoint => mapping(address dm => bool allowed)) private _dmApprovals;
Modifiers
It's important to have a look here at the modifiers, as they have a key role in organizing the access control mechanisms and restricting access to certain methods:
1. onlyDPOwner()
/**
* @notice Restricts access to the function, allowing only DataPoint admins
* @param dp DataPoint to check ownership of
*/
modifier onlyDPOwner(DataPoint dp) {
(uint32 chainId, address registry, ) = DataPoints.decode(dp);
ChainidTools.requireCurrentChain(chainId);
bool isAdmin = IDataPointRegistry(registry).isAdmin(dp, msg.sender);
if (!isAdmin) revert InvalidDataPointAdmin(dp, msg.sender);
_;
}
The Data Point structure contains important information about the chain where it should be processed and the registry where it was created. Since the Nexera Standard enables interoperability and Data Points store the source chain ID, we retrieve the chain ID to verify if we are on the correct destination chain. Additionally, we pull the registry address to check if the sender of the message is an admin of the Data Point. This ensures that only trusted users can modify the storage associated with a Data Point.
2. onlyApprovedDM()
/**
* @notice Allows access only to DataManagers which was previously approved
* @param dp DataPoint to check DataManager approval for
*/
modifier onlyApprovedDM(DataPoint dp) {
bool approved = _dmApprovals[dp][msg.sender];
if (!approved) revert DataManagerNotApproved(dp, msg.sender);
_;
}
Only whitelisted Data Managers are permitted to transact through the Data Index to Data Points. Here, we check if the message sender is an authorized Data Manager.
Even though, methods defined in the reference implementation to manage permissions and forward user operations can be summarized in 4 general methods isApprovedDataManager()
, allowDataManager()
, read()
and write()
, we've also added IDManager interface to generate Data Index user identifier and get information about them.
The methods inherited from IIDManager
both provide information about the owner for a DataIndex or generates and ID for the user and are:
///@inheritdoc IIDManager
function diid(address account, DataPoint) external pure returns (bytes32) {
return bytes32(uint256(uint160(account)));
}
///@inheritdoc IIDManager
function ownerOf(bytes32 diid_) external view returns (uint32, address) {
if (diid_ & PREFIX_MASK != 0) revert IncorrectIdentifier(diid_); // Require first 12 bytes empty, leaving only 20 bytes of address non-empty
address account = address(uint160(uint256(diid_)));
if (account == address(0)) revert IncorrectIdentifier(diid_);
return (ChainidTools.chainid(), account);
}