feat: add universal resource policy profile registry
This commit is contained in:
64
contracts/universal-resource/PolicyProfileRegistry.sol
Normal file
64
contracts/universal-resource/PolicyProfileRegistry.sol
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
|
||||
|
||||
/**
|
||||
* @title PolicyProfileRegistry
|
||||
* @notice Minimal on-chain anchor for URA `policyProfileId` rows (content hash + version + effective time).
|
||||
* Pair with off-chain `config/universal-resource-activation/policy-profiles.json` and
|
||||
* `scripts/ura/policy-profiles-content-hash.mjs` in the proxmox repo. Not a full GRU M00 diamond.
|
||||
*/
|
||||
contract PolicyProfileRegistry is AccessControl {
|
||||
bytes32 public constant PUBLISHER_ROLE = keccak256("PUBLISHER_ROLE");
|
||||
|
||||
struct Record {
|
||||
bytes32 contentHash;
|
||||
uint64 version;
|
||||
uint256 effectiveFrom;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping(bytes32 => Record) private _records;
|
||||
|
||||
event ProfilePublished(
|
||||
bytes32 indexed profileKey,
|
||||
string policyProfileId,
|
||||
bytes32 contentHash,
|
||||
uint64 version,
|
||||
uint256 effectiveFrom
|
||||
);
|
||||
|
||||
constructor(address admin) {
|
||||
require(admin != address(0), "PolicyProfileRegistry: admin");
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
||||
_grantRole(PUBLISHER_ROLE, admin);
|
||||
}
|
||||
|
||||
function profileKey(string calldata policyProfileId) public pure returns (bytes32) {
|
||||
return keccak256(bytes(policyProfileId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentHash keccak256 of canonical JSON for this profile row (see proxmox hash script)
|
||||
*/
|
||||
function publishProfile(
|
||||
string calldata policyProfileId,
|
||||
bytes32 contentHash,
|
||||
uint64 version,
|
||||
uint256 effectiveFrom
|
||||
) external onlyRole(PUBLISHER_ROLE) {
|
||||
bytes32 key = profileKey(policyProfileId);
|
||||
_records[key] = Record({
|
||||
contentHash: contentHash,
|
||||
version: version,
|
||||
effectiveFrom: effectiveFrom,
|
||||
exists: true
|
||||
});
|
||||
emit ProfilePublished(key, policyProfileId, contentHash, version, effectiveFrom);
|
||||
}
|
||||
|
||||
function getRecord(string calldata policyProfileId) external view returns (Record memory) {
|
||||
return _records[profileKey(policyProfileId)];
|
||||
}
|
||||
}
|
||||
21
script/universal-resource/DeployPolicyProfileRegistry.s.sol
Normal file
21
script/universal-resource/DeployPolicyProfileRegistry.s.sol
Normal file
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Script, console2} from "forge-std/Script.sol";
|
||||
import {PolicyProfileRegistry} from "../../contracts/universal-resource/PolicyProfileRegistry.sol";
|
||||
|
||||
/// @notice Deploy standalone URA policy profile anchor (not full GRU M00). Set POLICY_PROFILE_REGISTRY_ADMIN or defaults to deployer.
|
||||
contract DeployPolicyProfileRegistry is Script {
|
||||
function run() external {
|
||||
uint256 pk = vm.envUint("PRIVATE_KEY");
|
||||
address deployer = vm.addr(pk);
|
||||
address admin = vm.envOr("POLICY_PROFILE_REGISTRY_ADMIN", deployer);
|
||||
|
||||
vm.startBroadcast(pk);
|
||||
PolicyProfileRegistry reg = new PolicyProfileRegistry(admin);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console2.log("PolicyProfileRegistry", address(reg));
|
||||
console2.log("admin", admin);
|
||||
}
|
||||
}
|
||||
29
test/universal-resource/PolicyProfileRegistry.t.sol
Normal file
29
test/universal-resource/PolicyProfileRegistry.t.sol
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {PolicyProfileRegistry} from "../../contracts/universal-resource/PolicyProfileRegistry.sol";
|
||||
|
||||
contract PolicyProfileRegistryTest is Test {
|
||||
PolicyProfileRegistry internal reg;
|
||||
address internal admin = address(0xA11CE);
|
||||
|
||||
function setUp() public {
|
||||
vm.prank(admin);
|
||||
reg = new PolicyProfileRegistry(admin);
|
||||
}
|
||||
|
||||
function testPublishAndRead() public {
|
||||
bytes32 h = keccak256("profile-json");
|
||||
vm.prank(admin);
|
||||
reg.publishProfile("institutional_custody_skr_v1", h, 1, block.timestamp);
|
||||
PolicyProfileRegistry.Record memory r = reg.getRecord("institutional_custody_skr_v1");
|
||||
assertTrue(r.exists);
|
||||
assertEq(r.contentHash, h);
|
||||
assertEq(r.version, 1);
|
||||
}
|
||||
|
||||
function testProfileKeyStable() public view {
|
||||
assertEq(reg.profileKey("a"), keccak256(bytes("a")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user