@towns-protocol/contracts 0.0.416 → 0.0.418
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/scripts/deployments/diamonds/DeployAppRegistry.s.sol +12 -0
- package/scripts/deployments/facets/DeployValidationRegistry.s.sol +54 -0
- package/src/apps/facets/validation/IValidationRegistry.sol +152 -0
- package/src/apps/facets/validation/ValidationRegistryBase.sol +158 -0
- package/src/apps/facets/validation/ValidationRegistryFacet.sol +88 -0
- package/src/apps/facets/validation/ValidationRegistryStorage.sol +38 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@towns-protocol/contracts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.418",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"clean": "forge clean",
|
|
6
6
|
"compile": "forge build",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@layerzerolabs/oapp-evm": "^0.3.2",
|
|
34
34
|
"@openzeppelin/merkle-tree": "^1.0.8",
|
|
35
35
|
"@prb/test": "^0.6.4",
|
|
36
|
-
"@towns-protocol/prettier-config": "^0.0.
|
|
36
|
+
"@towns-protocol/prettier-config": "^0.0.418",
|
|
37
37
|
"@wagmi/cli": "^2.2.0",
|
|
38
38
|
"forge-std": "github:foundry-rs/forge-std#v1.10.0",
|
|
39
39
|
"prettier": "^3.5.3",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "735b3a67557584795c76b4b1632da911fcfe3abc"
|
|
57
57
|
}
|
|
@@ -19,6 +19,7 @@ import {DeploySpaceFactory} from "../diamonds/DeploySpaceFactory.s.sol";
|
|
|
19
19
|
import {DeploySimpleAppBeacon} from "../diamonds/DeploySimpleAppBeacon.s.sol";
|
|
20
20
|
import {DeployIdentityRegistry} from "../facets/DeployIdentityRegistry.s.sol";
|
|
21
21
|
import {DeployReputationRegistry} from "../facets/DeployReputationRegistry.s.sol";
|
|
22
|
+
import {DeployValidationRegistry} from "../facets/DeployValidationRegistry.s.sol";
|
|
22
23
|
|
|
23
24
|
// contracts
|
|
24
25
|
import {Diamond} from "@towns-protocol/diamond/src/Diamond.sol";
|
|
@@ -110,6 +111,7 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
110
111
|
facetHelper.add("AppFactoryFacet");
|
|
111
112
|
facetHelper.add("IdentityRegistryFacet");
|
|
112
113
|
facetHelper.add("ReputationRegistryFacet");
|
|
114
|
+
facetHelper.add("ValidationRegistryFacet");
|
|
113
115
|
|
|
114
116
|
facetHelper.deployBatch(deployer);
|
|
115
117
|
|
|
@@ -154,6 +156,13 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
154
156
|
DeployReputationRegistry.makeInitData(FEEDBACK_SCHEMA, RESPONSE_SCHEMA)
|
|
155
157
|
);
|
|
156
158
|
|
|
159
|
+
facet = facetHelper.getDeployedAddress("ValidationRegistryFacet");
|
|
160
|
+
addFacet(
|
|
161
|
+
makeCut(facet, FacetCutAction.Add, DeployValidationRegistry.selectors()),
|
|
162
|
+
facet,
|
|
163
|
+
DeployValidationRegistry.makeInitData()
|
|
164
|
+
);
|
|
165
|
+
|
|
157
166
|
address multiInit = facetHelper.getDeployedAddress("MultiInit");
|
|
158
167
|
|
|
159
168
|
return
|
|
@@ -192,6 +201,9 @@ contract DeployAppRegistry is IDiamondInitHelper, DiamondHelper, Deployer {
|
|
|
192
201
|
if (facetName.eq("IdentityRegistryFacet")) {
|
|
193
202
|
addCut(makeCut(facet, FacetCutAction.Add, DeployIdentityRegistry.selectors()));
|
|
194
203
|
}
|
|
204
|
+
if (facetName.eq("ValidationRegistryFacet")) {
|
|
205
|
+
addCut(makeCut(facet, FacetCutAction.Add, DeployValidationRegistry.selectors()));
|
|
206
|
+
}
|
|
195
207
|
}
|
|
196
208
|
|
|
197
209
|
return baseFacets();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IDiamond} from "@towns-protocol/diamond/src/IDiamond.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {LibDeploy} from "@towns-protocol/diamond/src/utils/LibDeploy.sol";
|
|
9
|
+
|
|
10
|
+
// contracts
|
|
11
|
+
import {ValidationRegistryFacet} from "src/apps/facets/validation/ValidationRegistryFacet.sol";
|
|
12
|
+
import {DynamicArrayLib} from "solady/utils/DynamicArrayLib.sol";
|
|
13
|
+
|
|
14
|
+
library DeployValidationRegistry {
|
|
15
|
+
using DynamicArrayLib for DynamicArrayLib.DynamicArray;
|
|
16
|
+
|
|
17
|
+
function selectors() internal pure returns (bytes4[] memory res) {
|
|
18
|
+
uint256 selectorsCount = 6;
|
|
19
|
+
DynamicArrayLib.DynamicArray memory arr = DynamicArrayLib.p().reserve(selectorsCount);
|
|
20
|
+
|
|
21
|
+
arr.p(ValidationRegistryFacet.validationRequest.selector);
|
|
22
|
+
arr.p(ValidationRegistryFacet.validationResponse.selector);
|
|
23
|
+
arr.p(ValidationRegistryFacet.getValidationStatus.selector);
|
|
24
|
+
arr.p(ValidationRegistryFacet.getSummary.selector);
|
|
25
|
+
arr.p(ValidationRegistryFacet.getAgentValidations.selector);
|
|
26
|
+
arr.p(ValidationRegistryFacet.getValidatorRequests.selector);
|
|
27
|
+
|
|
28
|
+
bytes32[] memory selectors_ = arr.asBytes32Array();
|
|
29
|
+
|
|
30
|
+
assembly ("memory-safe") {
|
|
31
|
+
res := selectors_
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeCut(
|
|
36
|
+
address facetAddress,
|
|
37
|
+
IDiamond.FacetCutAction action
|
|
38
|
+
) internal pure returns (IDiamond.FacetCut memory) {
|
|
39
|
+
return
|
|
40
|
+
IDiamond.FacetCut({
|
|
41
|
+
action: action,
|
|
42
|
+
facetAddress: facetAddress,
|
|
43
|
+
functionSelectors: selectors()
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeInitData() internal pure returns (bytes memory) {
|
|
48
|
+
return abi.encodeCall(ValidationRegistryFacet.__ValidationRegistryFacet_init, ());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function deploy() internal returns (address) {
|
|
52
|
+
return LibDeploy.deployCode("ValidationRegistryFacet.sol", "");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
|
|
6
|
+
// libraries
|
|
7
|
+
|
|
8
|
+
// contracts
|
|
9
|
+
|
|
10
|
+
/// @title IValidationRegistryBase
|
|
11
|
+
/// @notice Base interface for validation registry events
|
|
12
|
+
/// @dev Implements ERC-8004 validation event standards for trustless agent verification
|
|
13
|
+
interface IValidationRegistryBase {
|
|
14
|
+
/// @notice Emitted when an agent requests validation from a validator
|
|
15
|
+
/// @dev This event is emitted by validationRequest() and enables tracking of all validation requests
|
|
16
|
+
/// @param validatorAddress The address of the validator smart contract that will perform the validation
|
|
17
|
+
/// @param agentId The unique identifier of the agent requesting validation (ERC-721 tokenId from Identity Registry)
|
|
18
|
+
/// @param requestUri The URI pointing to off-chain data containing inputs, outputs, and all information needed for validation
|
|
19
|
+
/// @param requestHash The KECCAK-256 commitment hash of the request data (optional if requestUri is IPFS)
|
|
20
|
+
event ValidationRequest(
|
|
21
|
+
address indexed validatorAddress,
|
|
22
|
+
uint256 indexed agentId,
|
|
23
|
+
string requestUri,
|
|
24
|
+
bytes32 indexed requestHash
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/// @notice Emitted when a validator responds to a validation request
|
|
28
|
+
/// @dev This event can be emitted multiple times for the same requestHash to support progressive validation states
|
|
29
|
+
/// @param validatorAddress The address of the validator providing the response
|
|
30
|
+
/// @param agentId The unique identifier of the agent being validated
|
|
31
|
+
/// @param requestHash The hash of the original validation request being responded to
|
|
32
|
+
/// @param response The validation result (0-100 scale: 0=failed, 100=passed, or intermediate values for spectrum outcomes)
|
|
33
|
+
/// @param responseUri The URI pointing to off-chain evidence or audit trail of the validation (optional)
|
|
34
|
+
/// @param responseHash The KECCAK-256 hash of the response data (optional if responseUri is IPFS, use bytes32(0) if not provided)
|
|
35
|
+
/// @param tag Custom categorization or additional metadata for the validation (optional, e.g., "soft-finality", "hard-finality")
|
|
36
|
+
event ValidationResponse(
|
|
37
|
+
address indexed validatorAddress,
|
|
38
|
+
uint256 indexed agentId,
|
|
39
|
+
bytes32 indexed requestHash,
|
|
40
|
+
uint8 response,
|
|
41
|
+
string responseUri,
|
|
42
|
+
bytes32 responseHash,
|
|
43
|
+
bytes32 tag
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
error ValidationRegistry__InvalidValidator();
|
|
47
|
+
error ValidationRegistry__InvalidAgent();
|
|
48
|
+
error ValidationRegistry__RequestAlreadyExists();
|
|
49
|
+
error ValidationRegistry__AgentNotExists();
|
|
50
|
+
error ValidationRegistry__NotAuthorized();
|
|
51
|
+
error ValidationRegistry__RequestNotFound();
|
|
52
|
+
error ValidationRegistry__InvalidResponseScore();
|
|
53
|
+
error ValidationRegistry__ZeroResponseRequiresMetadata();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// @title IValidationRegistry
|
|
57
|
+
/// @notice Interface for the ERC-8004 Validation Registry enabling trustless agent verification
|
|
58
|
+
/// @dev This registry enables agents to request verification of their work through various trust models:
|
|
59
|
+
/// stake-secured inference re-execution, zkML verifiers, TEE oracles, or trusted judges.
|
|
60
|
+
/// The identityRegistry address is set at deployment and accessible via getIdentityRegistry().
|
|
61
|
+
/// All validation data is stored on-chain for composability and transparency.
|
|
62
|
+
interface IValidationRegistry is IValidationRegistryBase {
|
|
63
|
+
/// @notice Request validation of agent work from a validator smart contract
|
|
64
|
+
/// @dev MUST be called by the owner or operator of the agentId.
|
|
65
|
+
/// The requestUri should contain all information needed for validation including inputs and outputs.
|
|
66
|
+
/// The requestHash is a KECCAK-256 commitment to the request data (optional if requestUri is IPFS).
|
|
67
|
+
/// Emits a ValidationRequest event upon success.
|
|
68
|
+
/// @param validatorAddress The address of the validator smart contract that will perform the validation (mandatory)
|
|
69
|
+
/// @param agentId The unique identifier of the agent requesting validation (mandatory, must be validly registered)
|
|
70
|
+
/// @param requestUri The URI pointing to off-chain validation data (mandatory)
|
|
71
|
+
/// @param requestHash The KECCAK-256 hash of the request data (mandatory unless requestUri is content-addressable like IPFS)
|
|
72
|
+
function validationRequest(
|
|
73
|
+
address validatorAddress,
|
|
74
|
+
uint256 agentId,
|
|
75
|
+
string calldata requestUri,
|
|
76
|
+
bytes32 requestHash
|
|
77
|
+
) external;
|
|
78
|
+
|
|
79
|
+
/// @notice Submit a validation response for a previously requested validation
|
|
80
|
+
/// @dev MUST be called by the validatorAddress specified in the original ValidationRequest.
|
|
81
|
+
/// Can be called multiple times for the same requestHash to support use cases like
|
|
82
|
+
/// progressive validation states or updates to validation status.
|
|
83
|
+
/// Stores requestHash, validatorAddress, agentId, response, lastUpdate, and tag on-chain.
|
|
84
|
+
/// Emits a ValidationResponse event upon success.
|
|
85
|
+
/// @param requestHash The hash of the validation request being responded to (mandatory)
|
|
86
|
+
/// @param response The validation result on a 0-100 scale where 0=failed, 100=passed, or intermediate values (mandatory)
|
|
87
|
+
/// @param responseUri The URI pointing to off-chain evidence or audit trail (optional, use empty string if not provided)
|
|
88
|
+
/// @param responseHash The KECCAK-256 hash of the response data (optional if responseUri is IPFS, use bytes32(0) if not provided)
|
|
89
|
+
/// @param tag Custom categorization or metadata for this validation response (optional, use bytes32(0) if not provided)
|
|
90
|
+
function validationResponse(
|
|
91
|
+
bytes32 requestHash,
|
|
92
|
+
uint8 response,
|
|
93
|
+
string calldata responseUri,
|
|
94
|
+
bytes32 responseHash,
|
|
95
|
+
bytes32 tag
|
|
96
|
+
) external;
|
|
97
|
+
|
|
98
|
+
/// @notice Retrieve the current validation status for a specific request
|
|
99
|
+
/// @dev Returns the stored validation data for the given requestHash.
|
|
100
|
+
/// If no validation exists, returns zero values.
|
|
101
|
+
/// @param requestHash The hash of the validation request to query
|
|
102
|
+
/// @return validatorAddress The address of the validator that provided the response
|
|
103
|
+
/// @return agentId The unique identifier of the agent that was validated
|
|
104
|
+
/// @return response The validation result (0-100 scale)
|
|
105
|
+
/// @return tag The custom tag associated with this validation response
|
|
106
|
+
/// @return lastUpdate The timestamp of the last validation response update
|
|
107
|
+
function getValidationStatus(
|
|
108
|
+
bytes32 requestHash
|
|
109
|
+
)
|
|
110
|
+
external
|
|
111
|
+
view
|
|
112
|
+
returns (
|
|
113
|
+
address validatorAddress,
|
|
114
|
+
uint256 agentId,
|
|
115
|
+
uint8 response,
|
|
116
|
+
bytes32 tag,
|
|
117
|
+
uint256 lastUpdate
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
/// @notice Returns aggregated validation statistics for an agent
|
|
121
|
+
/// @dev Provides on-chain composable reputation metrics. The agentId parameter is mandatory;
|
|
122
|
+
/// validatorAddresses and tag are optional filters to narrow results.
|
|
123
|
+
/// Use empty array for validatorAddresses and bytes32(0) for tag to include all validations.
|
|
124
|
+
/// @param agentId The agent ID to get statistics for (mandatory)
|
|
125
|
+
/// @param validatorAddresses Array of validator addresses to filter by (optional, use empty array for no filter)
|
|
126
|
+
/// @param tag The tag to filter by (optional, use bytes32(0) for no filter)
|
|
127
|
+
/// @return count The total number of validations matching the filters
|
|
128
|
+
/// @return avgResponse The average validation response across all matching validations (0-100 scale)
|
|
129
|
+
function getSummary(
|
|
130
|
+
uint256 agentId,
|
|
131
|
+
address[] calldata validatorAddresses,
|
|
132
|
+
bytes32 tag
|
|
133
|
+
) external view returns (uint64 count, uint8 avgResponse);
|
|
134
|
+
|
|
135
|
+
/// @notice Returns all validation request hashes for a specific agent
|
|
136
|
+
/// @dev Useful for discovering the complete validation history of an agent.
|
|
137
|
+
/// The returned hashes can be used with getValidationStatus() to retrieve full details.
|
|
138
|
+
/// @param agentId The agent ID to query validation requests for
|
|
139
|
+
/// @return requestHashes Array of all validation request hashes associated with this agent
|
|
140
|
+
function getAgentValidations(
|
|
141
|
+
uint256 agentId
|
|
142
|
+
) external view returns (bytes32[] memory requestHashes);
|
|
143
|
+
|
|
144
|
+
/// @notice Returns all validation request hashes handled by a specific validator
|
|
145
|
+
/// @dev Useful for discovering all validations performed by a validator contract.
|
|
146
|
+
/// The returned hashes can be used with getValidationStatus() to retrieve full details.
|
|
147
|
+
/// @param validatorAddress The validator address to query validation requests for
|
|
148
|
+
/// @return requestHashes Array of all validation request hashes handled by this validator
|
|
149
|
+
function getValidatorRequests(
|
|
150
|
+
address validatorAddress
|
|
151
|
+
) external view returns (bytes32[] memory requestHashes);
|
|
152
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IValidationRegistryBase} from "./IValidationRegistry.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
9
|
+
import {ValidationRegistryStorage, ValidationStatus} from "./ValidationRegistryStorage.sol";
|
|
10
|
+
import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
|
|
11
|
+
|
|
12
|
+
// contracts
|
|
13
|
+
import {ERC721ABase} from "../../../diamond/facets/token/ERC721A/ERC721ABase.sol";
|
|
14
|
+
|
|
15
|
+
abstract contract ValidationRegistryBase is IValidationRegistryBase, ERC721ABase {
|
|
16
|
+
using CustomRevert for bytes4;
|
|
17
|
+
using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
|
|
18
|
+
|
|
19
|
+
function _requestValidation(
|
|
20
|
+
address validatorAddress,
|
|
21
|
+
uint256 agentId,
|
|
22
|
+
string calldata requestUri,
|
|
23
|
+
bytes32 requestHash
|
|
24
|
+
) internal {
|
|
25
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
26
|
+
if ($.validations[requestHash].validatorAddress != address(0))
|
|
27
|
+
ValidationRegistry__RequestAlreadyExists.selector.revertWith();
|
|
28
|
+
|
|
29
|
+
ValidationStatus storage validation = $.validations[requestHash];
|
|
30
|
+
validation.validatorAddress = validatorAddress;
|
|
31
|
+
validation.agentId = agentId;
|
|
32
|
+
validation.lastUpdate = block.timestamp;
|
|
33
|
+
|
|
34
|
+
$.requestByAgent[agentId].add(requestHash);
|
|
35
|
+
$.requestByValidator[validatorAddress].add(requestHash);
|
|
36
|
+
|
|
37
|
+
emit ValidationRequest(validatorAddress, agentId, requestUri, requestHash);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function _responseValidation(
|
|
41
|
+
bytes32 requestHash,
|
|
42
|
+
uint8 response,
|
|
43
|
+
string calldata responseUri,
|
|
44
|
+
bytes32 responseHash,
|
|
45
|
+
bytes32 tag
|
|
46
|
+
) internal {
|
|
47
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
48
|
+
ValidationStatus storage val = $.validations[requestHash];
|
|
49
|
+
if (val.validatorAddress == address(0))
|
|
50
|
+
ValidationRegistry__RequestNotFound.selector.revertWith();
|
|
51
|
+
if (msg.sender != val.validatorAddress)
|
|
52
|
+
ValidationRegistry__NotAuthorized.selector.revertWith();
|
|
53
|
+
if (response > 100) ValidationRegistry__InvalidResponseScore.selector.revertWith();
|
|
54
|
+
if (response == 0 && responseHash == bytes32(0) && tag == bytes32(0)) {
|
|
55
|
+
ValidationRegistry__ZeroResponseRequiresMetadata.selector.revertWith();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
val.response = response;
|
|
59
|
+
val.responseHash = responseHash;
|
|
60
|
+
val.tag = tag;
|
|
61
|
+
val.lastUpdate = block.timestamp;
|
|
62
|
+
|
|
63
|
+
emit ValidationResponse(
|
|
64
|
+
val.validatorAddress,
|
|
65
|
+
val.agentId,
|
|
66
|
+
requestHash,
|
|
67
|
+
response,
|
|
68
|
+
responseUri,
|
|
69
|
+
responseHash,
|
|
70
|
+
tag
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _getValidationStatus(
|
|
75
|
+
bytes32 requestHash
|
|
76
|
+
) internal view returns (ValidationStatus storage) {
|
|
77
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
78
|
+
return $.validations[requestHash];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function _getSummary(
|
|
82
|
+
uint256 agentId,
|
|
83
|
+
address[] calldata validatorAddresses,
|
|
84
|
+
bytes32 tag
|
|
85
|
+
) internal view returns (uint64 count, uint8 avgResponse) {
|
|
86
|
+
uint256 totalResponse;
|
|
87
|
+
|
|
88
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
89
|
+
EnumerableSetLib.Bytes32Set storage requests = $.requestByAgent[agentId];
|
|
90
|
+
|
|
91
|
+
for (uint256 i; i < requests.length(); ++i) {
|
|
92
|
+
// Fetch the validation status for the current requestHash
|
|
93
|
+
ValidationStatus storage val = $.validations[requests.at(i)];
|
|
94
|
+
|
|
95
|
+
// If no validatorAddresses filter is provided (i.e., length == 0), match any validator.
|
|
96
|
+
// Otherwise, check if the validatorAddress matches any address in the filter array.
|
|
97
|
+
bool matchValidator = (validatorAddresses.length == 0);
|
|
98
|
+
if (!matchValidator) {
|
|
99
|
+
// Loop through validatorAddresses to see if current validation was made by any provided validator
|
|
100
|
+
for (uint256 j; j < validatorAddresses.length; ++j) {
|
|
101
|
+
if (val.validatorAddress == validatorAddresses[j]) {
|
|
102
|
+
matchValidator = true;
|
|
103
|
+
break; // Stop searching if a match is found
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Tag filtering: If a zero tag is provided (no filtering), any tag matches.
|
|
109
|
+
// Otherwise, only include validations whose tag matches the requested tag.
|
|
110
|
+
bool matchTag = (tag == bytes32(0)) || (val.tag == tag);
|
|
111
|
+
|
|
112
|
+
// Check if a response has been given: at least one of (response, responseHash, tag) must be non-zero
|
|
113
|
+
// This distinguishes between "request with no response" and "request with response=0"
|
|
114
|
+
bool hasResponse = (val.response > 0) ||
|
|
115
|
+
(val.responseHash != bytes32(0)) ||
|
|
116
|
+
(val.tag != bytes32(0));
|
|
117
|
+
|
|
118
|
+
// Only count responses that matched validator and tag criteria, and have actually been responded to
|
|
119
|
+
if (matchValidator && matchTag && hasResponse) {
|
|
120
|
+
totalResponse += val.response; // Add the response value for averaging
|
|
121
|
+
count++; // Increment the count of included responses
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
avgResponse = count > 0 ? uint8(totalResponse / count) : 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function _getAgentValidations(
|
|
129
|
+
uint256 agentId
|
|
130
|
+
) internal view returns (bytes32[] memory requestHashes) {
|
|
131
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
132
|
+
return $.requestByAgent[agentId].values();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function _getValidatorRequests(
|
|
136
|
+
address validatorAddress
|
|
137
|
+
) internal view returns (bytes32[] memory requestHashes) {
|
|
138
|
+
ValidationRegistryStorage.Layout storage $ = ValidationRegistryStorage.getLayout();
|
|
139
|
+
return $.requestByValidator[validatorAddress].values();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// @dev Check validation request inputs
|
|
143
|
+
function _verifyValidationRequest(address validatorAddress, uint256 agentId) internal view {
|
|
144
|
+
if (validatorAddress == address(0))
|
|
145
|
+
ValidationRegistry__InvalidValidator.selector.revertWith();
|
|
146
|
+
if (agentId == 0) ValidationRegistry__InvalidAgent.selector.revertWith();
|
|
147
|
+
if (!_exists(agentId)) ValidationRegistry__AgentNotExists.selector.revertWith();
|
|
148
|
+
|
|
149
|
+
address owner = _ownerOf(agentId);
|
|
150
|
+
if (
|
|
151
|
+
msg.sender != owner &&
|
|
152
|
+
!_isApprovedForAll(owner, msg.sender) &&
|
|
153
|
+
msg.sender != _getApproved(agentId)
|
|
154
|
+
) {
|
|
155
|
+
ValidationRegistry__NotAuthorized.selector.revertWith();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IValidationRegistry} from "./IValidationRegistry.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
9
|
+
import {ValidationStatus} from "./ValidationRegistryStorage.sol";
|
|
10
|
+
|
|
11
|
+
// contracts
|
|
12
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
13
|
+
import {ValidationRegistryBase} from "./ValidationRegistryBase.sol";
|
|
14
|
+
|
|
15
|
+
contract ValidationRegistryFacet is IValidationRegistry, Facet, ValidationRegistryBase {
|
|
16
|
+
using CustomRevert for bytes4;
|
|
17
|
+
|
|
18
|
+
function __ValidationRegistryFacet_init() external onlyInitializing {
|
|
19
|
+
__ValidationRegistryFacet_init_unchained();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function __ValidationRegistryFacet_init_unchained() internal {
|
|
23
|
+
_addInterface(type(IValidationRegistry).interfaceId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// @inheritdoc IValidationRegistry
|
|
27
|
+
function validationRequest(
|
|
28
|
+
address validatorAddress,
|
|
29
|
+
uint256 agentId,
|
|
30
|
+
string calldata requestUri,
|
|
31
|
+
bytes32 requestHash
|
|
32
|
+
) external {
|
|
33
|
+
_verifyValidationRequest(validatorAddress, agentId);
|
|
34
|
+
_requestValidation(validatorAddress, agentId, requestUri, requestHash);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// @inheritdoc IValidationRegistry
|
|
38
|
+
function validationResponse(
|
|
39
|
+
bytes32 requestHash,
|
|
40
|
+
uint8 response,
|
|
41
|
+
string calldata responseUri,
|
|
42
|
+
bytes32 responseHash,
|
|
43
|
+
bytes32 tag
|
|
44
|
+
) external {
|
|
45
|
+
_responseValidation(requestHash, response, responseUri, responseHash, tag);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @inheritdoc IValidationRegistry
|
|
49
|
+
function getValidationStatus(
|
|
50
|
+
bytes32 requestHash
|
|
51
|
+
)
|
|
52
|
+
external
|
|
53
|
+
view
|
|
54
|
+
returns (
|
|
55
|
+
address validatorAddress,
|
|
56
|
+
uint256 agentId,
|
|
57
|
+
uint8 response,
|
|
58
|
+
bytes32 tag,
|
|
59
|
+
uint256 lastUpdate
|
|
60
|
+
)
|
|
61
|
+
{
|
|
62
|
+
ValidationStatus memory val = _getValidationStatus(requestHash);
|
|
63
|
+
return (val.validatorAddress, val.agentId, val.response, val.tag, val.lastUpdate);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @inheritdoc IValidationRegistry
|
|
67
|
+
function getSummary(
|
|
68
|
+
uint256 agentId,
|
|
69
|
+
address[] calldata validatorAddresses,
|
|
70
|
+
bytes32 tag
|
|
71
|
+
) external view returns (uint64 count, uint8 avgResponse) {
|
|
72
|
+
return _getSummary(agentId, validatorAddresses, tag);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// @inheritdoc IValidationRegistry
|
|
76
|
+
function getAgentValidations(
|
|
77
|
+
uint256 agentId
|
|
78
|
+
) external view returns (bytes32[] memory requestHashes) {
|
|
79
|
+
return _getAgentValidations(agentId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// @inheritdoc IValidationRegistry
|
|
83
|
+
function getValidatorRequests(
|
|
84
|
+
address validatorAddress
|
|
85
|
+
) external view returns (bytes32[] memory requestHashes) {
|
|
86
|
+
return _getValidatorRequests(validatorAddress);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
|
|
6
|
+
// libraries
|
|
7
|
+
import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
|
|
8
|
+
|
|
9
|
+
// contracts
|
|
10
|
+
|
|
11
|
+
struct ValidationStatus {
|
|
12
|
+
address validatorAddress;
|
|
13
|
+
uint256 agentId;
|
|
14
|
+
uint8 response; // 0..100
|
|
15
|
+
bytes32 responseHash;
|
|
16
|
+
bytes32 tag;
|
|
17
|
+
uint256 lastUpdate;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
library ValidationRegistryStorage {
|
|
21
|
+
using EnumerableSetLib for EnumerableSetLib.Bytes32Set;
|
|
22
|
+
|
|
23
|
+
// keccak256(abi.encode(uint256(keccak256("apps.facets.validation.storage")) - 1)) & ~bytes32(uint256(0xff))
|
|
24
|
+
bytes32 internal constant STORAGE_SLOT =
|
|
25
|
+
0x81b3fc8d09a49d21221f3e1495c2f4652ebf044e3338065682b43c2012468400;
|
|
26
|
+
|
|
27
|
+
struct Layout {
|
|
28
|
+
mapping(bytes32 requestHash => ValidationStatus) validations;
|
|
29
|
+
mapping(uint256 agentId => EnumerableSetLib.Bytes32Set requestHashes) requestByAgent;
|
|
30
|
+
mapping(address validatorAddress => EnumerableSetLib.Bytes32Set requestHashes) requestByValidator;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getLayout() internal pure returns (Layout storage $) {
|
|
34
|
+
assembly {
|
|
35
|
+
$.slot := STORAGE_SLOT
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|