diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f64f0dde..8d7c4ce5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -44,4 +44,4 @@ jobs: uses: ./.github/actions/setup - name: Run tests - run: npm run test + run: npm run test:parallel diff --git a/abi/CommitManagerV1.json b/abi/CommitManagerV1.json index f3d59a8b..4f1d262a 100644 --- a/abi/CommitManagerV1.json +++ b/abi/CommitManagerV1.json @@ -41,6 +41,32 @@ "name": "CommitWindowClosed", "type": "error" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidScoreFunctionId", + "type": "error" + }, { "inputs": [ { diff --git a/abi/CommitManagerV1U1.json b/abi/CommitManagerV1U1.json index f169e941..a712867f 100644 --- a/abi/CommitManagerV1U1.json +++ b/abi/CommitManagerV1U1.json @@ -46,6 +46,32 @@ "name": "CommitWindowClosed", "type": "error" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidScoreFunctionId", + "type": "error" + }, { "inputs": [ { diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json new file mode 100644 index 00000000..4b9788b9 --- /dev/null +++ b/abi/CommitManagerV2.json @@ -0,0 +1,848 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "ClosestNodeNotInNeighborhood", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "commitWindowOpen", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "commitWindowClose", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "CommitWindowClosed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "closestNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leftAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rightAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidClosestNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeAdjacentIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "leftEdgeNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rightEdgeNodeAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidLeftEdgeNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "numberOfNodes", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "neighborhoodExpectedSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neighborhoodActualSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidNeighborhoodSize", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidProximityScoreFunctionsPairId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeAdjacentIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "rightEdgeNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leftEdgeNodeAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidRightEdgeNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidScoreFunctionId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + } + ], + "name": "NodeAlreadySubmittedCommit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "rank", + "type": "uint8" + } + ], + "name": "NodeNotAwarded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "ask", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "NodeNotInShardingTable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + } + ], + "name": "ServiceAgreementDoesntExist", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "epochsNumber", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "epochLength", + "type": "uint128" + } + ], + "name": "ServiceAgreementHasBeenExpired", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "uint40", + "name": "score", + "type": "uint40" + } + ], + "name": "CommitSubmitted", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "name": "getTopCommitSubmissions", + "outputs": [ + { + "components": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "type": "uint72" + }, + { + "internalType": "uint40", + "name": "score", + "type": "uint40" + } + ], + "internalType": "struct ServiceAgreementStructsV1.CommitSubmission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "identityStorage", + "outputs": [ + { + "internalType": "contract IdentityStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "name": "isCommitWindowOpen", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "linearSum", + "outputs": [ + { + "internalType": "contract LinearSum", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "log2pldsf", + "outputs": [ + { + "internalType": "contract Log2PLDSF", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "parametersStorage", + "outputs": [ + { + "internalType": "contract ParametersStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "profileStorage", + "outputs": [ + { + "internalType": "contract ProfileStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "reqs", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "serviceAgreementStorageProxy", + "outputs": [ + { + "internalType": "contract ServiceAgreementStorageProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "req", + "type": "bool" + } + ], + "name": "setReq", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shardingTableStorage", + "outputs": [ + { + "internalType": "contract ShardingTableStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStorage", + "outputs": [ + { + "internalType": "contract StakingStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "status", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "internalType": "struct ServiceAgreementStructsV1.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + } + ], + "internalType": "struct ServiceAgreementStructsV2.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/CommitManagerV2U1.json b/abi/CommitManagerV2U1.json new file mode 100644 index 00000000..eb79b779 --- /dev/null +++ b/abi/CommitManagerV2U1.json @@ -0,0 +1,1089 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "ClosestNodeNotInNeighborhood", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "commitWindowOpen", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "commitWindowClose", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "CommitWindowClosed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "closestNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leftAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rightAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidClosestNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeAdjacentIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "leftEdgeNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rightEdgeNodeAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidLeftEdgeNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "numberOfNodes", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "neighborhoodExpectedSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neighborhoodActualSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidNeighborhoodSize", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidProximityScoreFunctionsPairId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeAdjacentIndex", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "rightEdgeNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leftEdgeNodeAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidRightEdgeNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "agreementScoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidScoreFunctionId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "assetStorage", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "NoPendingUpdate", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + } + ], + "name": "NodeAlreadySubmittedCommit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "rank", + "type": "uint8" + } + ], + "name": "NodeNotAwarded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "ask", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "NodeNotInShardingTable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + } + ], + "name": "ServiceAgreementDoesntExist", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "epochsNumber", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "epochLength", + "type": "uint128" + } + ], + "name": "ServiceAgreementHasBeenExpired", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "uint40", + "name": "score", + "type": "uint40" + } + ], + "name": "CommitSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "state", + "type": "bytes32" + } + ], + "name": "StateFinalized", + "type": "event" + }, + { + "inputs": [], + "name": "contentAssetStorage", + "outputs": [ + { + "internalType": "contract ContentAssetStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + } + ], + "name": "getTopCommitSubmissions", + "outputs": [ + { + "components": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "type": "uint72" + }, + { + "internalType": "uint40", + "name": "score", + "type": "uint40" + } + ], + "internalType": "struct ServiceAgreementStructsV1.CommitSubmission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "identityStorage", + "outputs": [ + { + "internalType": "contract IdentityStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "name": "isCommitWindowOpen", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + } + ], + "name": "isUpdateCommitWindowOpen", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "linearSum", + "outputs": [ + { + "internalType": "contract LinearSum", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "log2pldsf", + "outputs": [ + { + "internalType": "contract Log2PLDSF", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "parametersStorage", + "outputs": [ + { + "internalType": "contract ParametersStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "profileStorage", + "outputs": [ + { + "internalType": "contract ProfileStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "reqs", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "serviceAgreementStorageProxy", + "outputs": [ + { + "internalType": "contract ServiceAgreementStorageProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "req", + "type": "bool" + } + ], + "name": "setReq", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shardingTableStorage", + "outputs": [ + { + "internalType": "contract ShardingTableStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStorage", + "outputs": [ + { + "internalType": "contract StakingStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "status", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "internalType": "struct ServiceAgreementStructsV1.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + } + ], + "internalType": "struct ServiceAgreementStructsV2.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + } + ], + "internalType": "struct ServiceAgreementStructsV1.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitUpdateCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "assetContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "epoch", + "type": "uint16" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "leftEdgeNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeNodeIndex", + "type": "uint72" + } + ], + "internalType": "struct ServiceAgreementStructsV2.CommitInputArgs", + "name": "args", + "type": "tuple" + } + ], + "name": "submitUpdateCommit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unfinalizedStateStorage", + "outputs": [ + { + "internalType": "contract UnfinalizedStateStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/IProximityScoreFunctionsPair.json b/abi/IProximityScoreFunctionsPair.json new file mode 100644 index 00000000..db3392a2 --- /dev/null +++ b/abi/IProximityScoreFunctionsPair.json @@ -0,0 +1,65 @@ +[ + { + "inputs": [ + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + } + ], + "name": "calculateDistance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "distance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDistance", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "maxNodesNumber", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "calculateScore", + "outputs": [ + { + "internalType": "uint40", + "name": "", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/LinearSum.json b/abi/LinearSum.json new file mode 100644 index 00000000..8f1b0842 --- /dev/null +++ b/abi/LinearSum.json @@ -0,0 +1,396 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "parameterName", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "parameterValue", + "type": "uint256" + } + ], + "name": "ParameterChanged", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + } + ], + "name": "calculateDistance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "leftEdgeNodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "closestEdgeNodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rightEdgeNodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + } + ], + "name": "calculateNeighborhoodBoundaryDistances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "distance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDistance", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "maxNodesNumber", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "calculateScore", + "outputs": [ + { + "internalType": "uint40", + "name": "", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "distanceScaleFactor", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getParameters", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hashingProxy", + "outputs": [ + { + "internalType": "contract HashingProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "id", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "distance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDistance", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "nodesCount", + "type": "uint72" + } + ], + "name": "normalizeDistance", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "normalizeStake", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parametersStorage", + "outputs": [ + { + "internalType": "contract ParametersStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "distanceScaleFactor_", + "type": "uint96" + } + ], + "name": "setDistanceScaleFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "stakeScaleFactor_", + "type": "uint96" + } + ], + "name": "setStakeScaleFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "w1_", + "type": "uint32" + } + ], + "name": "setW1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "w2_", + "type": "uint32" + } + ], + "name": "setW2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakeScaleFactor", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "w1", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "w2", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/NodeOperatorFeeChangesStorage.json b/abi/NodeOperatorFeeChangesStorage.json new file mode 100644 index 00000000..9933535b --- /dev/null +++ b/abi/NodeOperatorFeeChangesStorage.json @@ -0,0 +1,195 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint8", + "name": "newFee", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "createOperatorFeeChangeRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "delayFreePeriodEnd", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "deleteOperatorFeeChangeRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "getOperatorFeeChangeRequestNewFee", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "getOperatorFeeChangeRequestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "operatorFeeChangeRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "name": "operatorFeeChangeRequests", + "outputs": [ + { + "internalType": "uint8", + "name": "newFee", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "setDelayFreePeriodEnd", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/Profile.json b/abi/Profile.json index 3c599ab3..ccf5205d 100644 --- a/abi/Profile.json +++ b/abi/Profile.json @@ -49,6 +49,12 @@ "internalType": "bytes", "name": "nodeId", "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "sharesContractAddress", + "type": "address" } ], "name": "ProfileCreated", diff --git a/abi/ProximityScoringProxy.json b/abi/ProximityScoringProxy.json new file mode 100644 index 00000000..b71d3c2d --- /dev/null +++ b/abi/ProximityScoringProxy.json @@ -0,0 +1,341 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "address", + "name": "newContractAddress", + "type": "address" + } + ], + "name": "NewScoringFunctionContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "address", + "name": "newContractAddress", + "type": "address" + } + ], + "name": "ScoringFunctionContractUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "proximityFunctionId", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + } + ], + "name": "callProximityFunction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "keyword", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "callScoreFunction", + "outputs": [ + { + "internalType": "uint40", + "name": "", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "distance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxDistance", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "nodesCount", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "callScoreFunction", + "outputs": [ + { + "internalType": "uint40", + "name": "", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllScoreFunctions", + "outputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "id", + "type": "uint8" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "internalType": "struct UnorderedIndexableContractDynamicSetLib.Contract[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + } + ], + "name": "getScoreFunctionContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + } + ], + "name": "getScoreFunctionName", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + } + ], + "name": "isScoreFunction", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + } + ], + "name": "removeContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "internalType": "address", + "name": "scoringContractAddress", + "type": "address" + } + ], + "name": "setContractAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "status", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/ServiceAgreementV1.json b/abi/ServiceAgreementV1.json index a63f7f3e..0635d134 100644 --- a/abi/ServiceAgreementV1.json +++ b/abi/ServiceAgreementV1.json @@ -28,6 +28,11 @@ }, { "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, { "internalType": "uint256", "name": "amount", @@ -39,6 +44,11 @@ }, { "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, { "internalType": "uint256", "name": "amount", diff --git a/abi/ShardingTable.json b/abi/ShardingTable.json index 28a5cc14..e1c14277 100644 --- a/abi/ShardingTable.json +++ b/abi/ShardingTable.json @@ -87,7 +87,7 @@ "type": "uint96" } ], - "internalType": "struct ShardingTableStructs.NodeInfo[]", + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", "name": "", "type": "tuple[]" } @@ -133,7 +133,7 @@ "type": "uint96" } ], - "internalType": "struct ShardingTableStructs.NodeInfo[]", + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", "name": "", "type": "tuple[]" } diff --git a/abi/ShardingTableStorage.json b/abi/ShardingTableStorage.json index 61ac368d..e5232b6f 100644 --- a/abi/ShardingTableStorage.json +++ b/abi/ShardingTableStorage.json @@ -86,7 +86,7 @@ "type": "uint72" } ], - "internalType": "struct ShardingTableStructs.Node[]", + "internalType": "struct ShardingTableStructsV1.Node[]", "name": "", "type": "tuple[]" } @@ -122,7 +122,7 @@ "type": "uint72" } ], - "internalType": "struct ShardingTableStructs.Node", + "internalType": "struct ShardingTableStructsV1.Node", "name": "", "type": "tuple" } diff --git a/abi/ShardingTableStorageV2.json b/abi/ShardingTableStorageV2.json new file mode 100644 index 00000000..31b9d1b1 --- /dev/null +++ b/abi/ShardingTableStorageV2.json @@ -0,0 +1,529 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + } + ], + "name": "createNodeObject", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "decrementNodeIndex", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decrementNodesCount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "deleteNodeObject", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + } + ], + "name": "getAdjacentIdentityIdsByIndex", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "getHashRingPosition", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + } + ], + "name": "getHashRingPositionByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "firstIdentityId", + "type": "uint72" + }, + { + "internalType": "uint16", + "name": "nodesNumber", + "type": "uint16" + } + ], + "name": "getMultipleNodes", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "leftEdgeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeIndex", + "type": "uint72" + } + ], + "name": "getNeighborhoodBoundaryByIndexes", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "getNode", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + } + ], + "name": "getNodeByIndex", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "head", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "incrementNodeIndex", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "incrementNodesCount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "name": "indexToIdentityId", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "nodeExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + } + ], + "name": "nodeExistsByIndex", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nodesCount", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "setIdentityId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/ShardingTableV2.json b/abi/ShardingTableV2.json new file mode 100644 index 00000000..476d7bba --- /dev/null +++ b/abi/ShardingTableV2.json @@ -0,0 +1,341 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nextHashRingPosition", + "type": "uint256" + } + ], + "name": "InvalidIndexWithRespectToNextNode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "prevHashRingPosition", + "type": "uint256" + } + ], + "name": "InvalidIndexWithRespectToPreviousNode", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "ask", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "name": "NodeAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + } + ], + "name": "NodeRemoved", + "type": "event" + }, + { + "inputs": [], + "name": "getShardingTable", + "outputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "ask", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "startingIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nodesNumber", + "type": "uint72" + } + ], + "name": "getShardingTable", + "outputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "ask", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "stake", + "type": "uint96" + } + ], + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "insertNode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "insertNode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "profileStorage", + "outputs": [ + { + "internalType": "contract ProfileStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "removeNode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shardingTableStorage", + "outputs": [ + { + "internalType": "contract ShardingTableStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStorage", + "outputs": [ + { + "internalType": "contract StakingStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "status", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/StakingV2.json b/abi/StakingV2.json new file mode 100644 index 00000000..a16a9665 --- /dev/null +++ b/abi/StakingV2.json @@ -0,0 +1,750 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidOperatorFee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MaximumStakeExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "OnlyProfileAdminFunction", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "nowTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endTimestamp", + "type": "uint256" + } + ], + "name": "OperatorFeeChangeDelayPending", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "ProfileDoesntExist", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TooLowAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TooLowBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "nowTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endTimestamp", + "type": "uint256" + } + ], + "name": "WithdrawalPeriodPending", + "type": "error" + }, + { + "inputs": [], + "name": "WithdrawalWasntInitiated", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroSharesAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "oldAccumulatedOperatorFee", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "newAccumulatedOperatorFee", + "type": "uint96" + } + ], + "name": "AccumulatedOperatorFeeIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "operatorFee", + "type": "uint8" + } + ], + "name": "OperatorFeeChangeFinished", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "operatorFee", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "OperatorFeeChangeStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "serviceAgreementAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "nodeOperatorFee", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "delegatorsReward", + "type": "uint96" + } + ], + "name": "RewardCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sharesContractAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sharesBurnedAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalSupply", + "type": "uint256" + } + ], + "name": "SharesBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sharesContractAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sharesMintedAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalSupply", + "type": "uint256" + } + ], + "name": "SharesMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "oldStake", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "newStake", + "type": "uint96" + } + ], + "name": "StakeIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "oldStake", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "newStake", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "withdrawalPeriodEnd", + "type": "uint256" + } + ], + "name": "StakeWithdrawalStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "withdrawnStakeAmount", + "type": "uint96" + } + ], + "name": "StakeWithdrawn", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "agreementId", + "type": "bytes32" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "rewardAmount", + "type": "uint96" + } + ], + "name": "addReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "stakeAmount", + "type": "uint96" + } + ], + "name": "addStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "stakeAmount", + "type": "uint96" + } + ], + "name": "addStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "finishOperatorFeeChange", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "hub", + "outputs": [ + { + "internalType": "contract Hub", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "identityStorage", + "outputs": [ + { + "internalType": "contract IdentityStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorFeeChangesStorage", + "outputs": [ + { + "internalType": "contract NodeOperatorFeeChangesStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parametersStorage", + "outputs": [ + { + "internalType": "contract ParametersStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "profileStorage", + "outputs": [ + { + "internalType": "contract ProfileStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "serviceAgreementStorageProxy", + "outputs": [ + { + "internalType": "contract ServiceAgreementStorageProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shardingTableContract", + "outputs": [ + { + "internalType": "contract ShardingTableV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "shardingTableStorage", + "outputs": [ + { + "internalType": "contract ShardingTableStorageV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "slash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStorage", + "outputs": [ + { + "internalType": "contract StakingStorage", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint8", + "name": "newOperatorFee", + "type": "uint8" + } + ], + "name": "startOperatorFeeChange", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint96", + "name": "sharesToBurn", + "type": "uint96" + } + ], + "name": "startStakeWithdrawal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "status", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenContract", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "withdrawStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/contracts/v1/CommitManagerV1.sol b/contracts/v1/CommitManagerV1.sol index 6d70a68d..a8fa6b58 100644 --- a/contracts/v1/CommitManagerV1.sol +++ b/contracts/v1/CommitManagerV1.sol @@ -32,7 +32,9 @@ contract CommitManagerV1 is Named, Versioned, ContractStatus, Initializable { ); string private constant _NAME = "CommitManagerV1"; - string private constant _VERSION = "1.0.1"; + string private constant _VERSION = "1.0.2"; + + uint8 private constant _LOG2PLDSF_ID = 1; bool[4] public reqs = [false, false, false, false]; @@ -93,9 +95,7 @@ contract CommitManagerV1 is Named, Versioned, ContractStatus, Initializable { uint256 timeNow = block.timestamp; uint256 commitWindowDuration = (params.commitWindowDurationPerc() * epochLength) / 100; - if (epoch == 0) { - return timeNow < (startTime + commitWindowDuration); - } + if (epoch == 0) return timeNow < (startTime + commitWindowDuration); return (timeNow >= (startTime + epochLength * epoch) && timeNow < (startTime + epochLength * epoch + commitWindowDuration)); @@ -154,6 +154,14 @@ contract CommitManagerV1 is Named, Versioned, ContractStatus, Initializable { if (!sasProxy.agreementV1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); @@ -255,13 +263,13 @@ contract CommitManagerV1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); - if ((i == 0) && (refCommit.identityId == 0)) { + if ((i == 0) && (refCommit.identityId == 0)) // No head -> Setting new head sasProxy.setV1AgreementEpochSubmissionHead(agreementId, epoch, commitId); - } else if ((i == 0) && (score <= refCommit.score)) { + else if ((i == 0) && (score <= refCommit.score)) // There is a head with higher or equal score, add new commit on the right _linkCommits(agreementId, epoch, refCommit.identityId, identityId); - } else if ((i == 0) && (score > refCommit.score)) { + else if ((i == 0) && (score > refCommit.score)) { // There is a head with lower score, replace the head sasProxy.setV1AgreementEpochSubmissionHead(agreementId, epoch, commitId); _linkCommits(agreementId, epoch, identityId, refCommit.identityId); @@ -275,11 +283,10 @@ contract CommitManagerV1 is Named, Versioned, ContractStatus, Initializable { // [] <-> [H] <-> [X] ... [RC-] <-(NL)-> [NC] <-(NL)-> [RC] <-> [RC+] ... [C] <-> [] _linkCommits(agreementId, epoch, refCommit.prevIdentityId, identityId); _linkCommits(agreementId, epoch, identityId, refCommit.identityId); - } else { - // [] <-> [H] <-> [RC] <-> [] - // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] - _linkCommits(agreementId, epoch, refCommit.identityId, identityId); } + // [] <-> [H] <-> [RC] <-> [] + // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] + else _linkCommits(agreementId, epoch, refCommit.identityId, identityId); } function _linkCommits( diff --git a/contracts/v1/CommitManagerV1U1.sol b/contracts/v1/CommitManagerV1U1.sol index d08c5d44..0d81e6aa 100644 --- a/contracts/v1/CommitManagerV1U1.sol +++ b/contracts/v1/CommitManagerV1U1.sol @@ -21,6 +21,7 @@ import {Versioned} from "./interface/Versioned.sol"; import {ServiceAgreementStructsV1} from "./structs/ServiceAgreementStructsV1.sol"; import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "./errors/GeneralErrors.sol"; +import {ServiceAgreementErrorsV1} from "./errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV1U1} from "./errors/ServiceAgreementErrorsV1U1.sol"; contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { @@ -45,7 +46,9 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ); string private constant _NAME = "CommitManagerV1U1"; - string private constant _VERSION = "1.0.1"; + string private constant _VERSION = "1.0.2"; + + uint8 private constant _LOG2PLDSF_ID = 1; bool[6] public reqs = [false, false, false, false, false, false]; @@ -92,7 +95,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); uint256 startTime = sasProxy.getAgreementStartTime(agreementId); @@ -100,7 +103,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementHasBeenExpired( + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( agreementId, startTime, sasProxy.getAgreementEpochsNumber(agreementId), @@ -110,9 +113,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { uint256 timeNow = block.timestamp; uint256 commitWindowDuration = (params.commitWindowDurationPerc() * epochLength) / 100; - if (epoch == 0) { - return timeNow < (startTime + commitWindowDuration); - } + if (epoch == 0) return timeNow < (startTime + commitWindowDuration); return (timeNow >= (startTime + epochLength * epoch) && timeNow < (startTime + epochLength * epoch + commitWindowDuration)); @@ -128,9 +129,9 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); if (!sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementHasBeenExpired( + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( agreementId, sasProxy.getAgreementStartTime(agreementId), sasProxy.getAgreementEpochsNumber(agreementId), @@ -152,9 +153,9 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementHasBeenExpired( + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( agreementId, sasProxy.getAgreementStartTime(agreementId), sasProxy.getAgreementEpochsNumber(agreementId), @@ -196,7 +197,15 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ); if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); uint256 latestFinalizedStateIndex = AbstractAsset(args.assetContract).getAssertionIdsLength(args.tokenId) - 1; @@ -220,7 +229,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { ProfileStorage ps = profileStorage; - revert ServiceAgreementErrorsV1U1.NodeNotInShardingTable( + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( identityId, ps.getNodeId(identityId), ps.getAsk(identityId), @@ -257,9 +266,8 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { bytes32 unfinalizedState = uss.getUnfinalizedState(args.tokenId); uint256 unfinalizedStateIndex = generalAssetInterface.getAssertionIdsLength(args.tokenId); - if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) { + if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); - } ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; @@ -269,7 +277,15 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ); if (!sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); if (!reqs[2] && !isUpdateCommitWindowOpen(agreementId, args.epoch, unfinalizedStateIndex)) { uint256 commitWindowEnd = sasProxy.getUpdateCommitsDeadline( @@ -291,7 +307,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { if (!reqs[3] && !shardingTableStorage.nodeExists(identityId)) { ProfileStorage ps = profileStorage; - revert ServiceAgreementErrorsV1U1.NodeNotInShardingTable( + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( identityId, ps.getNodeId(identityId), ps.getAsk(identityId), @@ -324,9 +340,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { sasProxy.getCommitsCount(keccak256(abi.encodePacked(agreementId, args.epoch, unfinalizedStateIndex))) == parametersStorage.finalizationCommitsNumber() ) { - if (sasProxy.agreementV1Exists(agreementId)) { - sasProxy.migrateV1ServiceAgreement(agreementId); - } + if (sasProxy.agreementV1Exists(agreementId)) sasProxy.migrateV1ServiceAgreement(agreementId); sasProxy.setAgreementTokenAmount( agreementId, @@ -409,13 +423,13 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); - if ((i == 0) && (refCommit.identityId == 0)) { + if ((i == 0) && (refCommit.identityId == 0)) // No head -> Setting new head sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex, commitId); - } else if ((i == 0) && (score <= refCommit.score)) { + else if ((i == 0) && (score <= refCommit.score)) // There is a head with higher or equal score, add new commit on the right _linkCommits(agreementId, epoch, stateIndex, refCommit.identityId, identityId); - } else if ((i == 0) && (score > refCommit.score)) { + else if ((i == 0) && (score > refCommit.score)) { // There is a head with lower score, replace the head sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex, commitId); _linkCommits(agreementId, epoch, stateIndex, identityId, refCommit.identityId); @@ -429,11 +443,10 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { // [] <-> [H] <-> [X] ... [RC-] <-(NL)-> [NC] <-(NL)-> [RC] <-> [RC+] ... [C] <-> [] _linkCommits(agreementId, epoch, stateIndex, refCommit.prevIdentityId, identityId); _linkCommits(agreementId, epoch, stateIndex, identityId, refCommit.identityId); - } else { - // [] <-> [H] <-> [RC] <-> [] - // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] - _linkCommits(agreementId, epoch, stateIndex, refCommit.identityId, identityId); } + // [] <-> [H] <-> [RC] <-> [] + // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] + else _linkCommits(agreementId, epoch, stateIndex, refCommit.identityId, identityId); sasProxy.incrementCommitsCount(keccak256(abi.encodePacked(agreementId, epoch, stateIndex))); } diff --git a/contracts/v1/HubController.sol b/contracts/v1/HubController.sol index d94a6aa9..b26158bf 100644 --- a/contracts/v1/HubController.sol +++ b/contracts/v1/HubController.sol @@ -89,16 +89,14 @@ contract HubController is Named, Versioned, ContractStatus, Ownable { ) external onlyOwnerOrMultiSigOwner { if (hub.isContract(contractName)) { address oldContractAddress = hub.getContractAddress(contractName); - if (_isContract(oldContractAddress)) { + if (_isContract(oldContractAddress)) // solhint-disable-next-line no-empty-blocks try ContractStatus(hub.getContractAddress(contractName)).setStatus(false) {} catch {} - } } hub.setContractAddress(contractName, newContractAddress); - if (_isContract(newContractAddress)) { + if (_isContract(newContractAddress)) // solhint-disable-next-line no-empty-blocks try ContractStatus(newContractAddress).setStatus(true) {} catch {} - } } function setAssetStorageAddress( @@ -120,16 +118,14 @@ contract HubController is Named, Versioned, ContractStatus, Ownable { for (uint i; i < newContracts.length; ) { if (hub.isContract(newContracts[i].name)) { address oldContractAddress = hub.getContractAddress(newContracts[i].name); - if (_isContract(oldContractAddress)) { + if (_isContract(oldContractAddress)) // solhint-disable-next-line no-empty-blocks try ContractStatus(oldContractAddress).setStatus(false) {} catch {} - } } hub.setContractAddress(newContracts[i].name, newContracts[i].addr); - if (_isContract(newContracts[i].addr)) { + if (_isContract(newContracts[i].addr)) // solhint-disable-next-line no-empty-blocks try ContractStatus(newContracts[i].addr).setStatus(true) {} catch {} - } unchecked { i++; } @@ -214,9 +210,7 @@ contract HubController is Named, Versioned, ContractStatus, Ownable { function _isMultiSigOwner(address multiSigAddress) internal view returns (bool) { try ICustodian(multiSigAddress).getOwners() returns (address[] memory multiSigOwners) { for (uint i = 0; i < multiSigOwners.length; i++) { - if (msg.sender == multiSigOwners[i]) { - return true; - } + if (msg.sender == multiSigOwners[i]) return true; } // solhint-disable-next-line no-empty-blocks } catch {} diff --git a/contracts/v1/Identity.sol b/contracts/v1/Identity.sol index ee89d087..083d301d 100644 --- a/contracts/v1/Identity.sol +++ b/contracts/v1/Identity.sol @@ -82,9 +82,7 @@ contract Identity is Named, Versioned, ContractStatus, Initializable { ids.addKey(identityId, key, keyPurpose, keyType); - if (keyPurpose == OPERATIONAL_KEY) { - ids.setOperationalKeyIdentityId(key, identityId); - } + if (keyPurpose == OPERATIONAL_KEY) ids.setOperationalKeyIdentityId(key, identityId); } function removeKey(uint72 identityId, bytes32 key) external onlyAdmin(identityId) { @@ -109,9 +107,7 @@ contract Identity is Named, Versioned, ContractStatus, Initializable { ids.removeKey(identityId, key); - if (purpose == OPERATIONAL_KEY) { - ids.removeOperationalKeyIdentityId(key); - } + if (purpose == OPERATIONAL_KEY) ids.removeOperationalKeyIdentityId(key); } function _checkAdmin(uint72 identityId) internal view virtual { diff --git a/contracts/v1/Profile.sol b/contracts/v1/Profile.sol index d71559f2..0ec0ae10 100644 --- a/contracts/v1/Profile.sol +++ b/contracts/v1/Profile.sol @@ -18,12 +18,12 @@ import {UnorderedIndexableContractDynamicSetLib} from "./utils/UnorderedIndexabl import {ADMIN_KEY, OPERATIONAL_KEY} from "./constants/IdentityConstants.sol"; contract Profile is Named, Versioned, ContractStatus, Initializable { - event ProfileCreated(uint72 indexed identityId, bytes nodeId); + event ProfileCreated(uint72 indexed identityId, bytes nodeId, address sharesContractAddress); event ProfileDeleted(uint72 indexed identityId); event AskUpdated(uint72 indexed identityId, bytes nodeId, uint96 ask); string private constant _NAME = "Profile"; - string private constant _VERSION = "1.0.2"; + string private constant _VERSION = "1.0.3"; HashingProxy public hashingProxy; Identity public identityContract; @@ -104,7 +104,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable { ps.createProfile(identityId, nodeId, address(sharesContract)); _setAvailableNodeAddresses(identityId); - emit ProfileCreated(identityId, nodeId); + emit ProfileCreated(identityId, nodeId, address(sharesContract)); } function setAsk(uint72 identityId, uint96 ask) external onlyIdentityOwner(identityId) { @@ -229,8 +229,6 @@ contract Profile is Named, Versioned, ContractStatus, Initializable { function _checkWhitelist() internal view virtual { WhitelistStorage ws = whitelistStorage; - if (ws.whitelistingEnabled()) { - require(ws.whitelisted(msg.sender), "Address isn't whitelisted"); - } + if (ws.whitelistingEnabled()) require(ws.whitelisted(msg.sender), "Address isn't whitelisted"); } } diff --git a/contracts/v1/ProofManagerV1U1.sol b/contracts/v1/ProofManagerV1U1.sol index ee3ddcdc..d6b5afd4 100644 --- a/contracts/v1/ProofManagerV1U1.sol +++ b/contracts/v1/ProofManagerV1U1.sol @@ -17,6 +17,7 @@ import {Versioned} from "./interface/Versioned.sol"; import {ServiceAgreementStructsV1} from "./structs/ServiceAgreementStructsV1.sol"; import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "./errors/GeneralErrors.sol"; +import {ServiceAgreementErrorsV1} from "./errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV1U1} from "./errors/ServiceAgreementErrorsV1U1.sol"; import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; @@ -71,12 +72,12 @@ contract ProofManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); uint256 startTime = sasProxy.getAgreementStartTime(agreementId); if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementHasBeenExpired( + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( agreementId, startTime, sasProxy.getAgreementEpochsNumber(agreementId), @@ -111,7 +112,7 @@ contract ProofManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ); if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); uint256 latestFinalizedStateIndex = AbstractAsset(args.assetContract).getAssertionIdsLength(args.tokenId) - 1; diff --git a/contracts/v1/ServiceAgreementV1.sol b/contracts/v1/ServiceAgreementV1.sol index bf71bd4b..dfb54c99 100644 --- a/contracts/v1/ServiceAgreementV1.sol +++ b/contracts/v1/ServiceAgreementV1.sol @@ -16,6 +16,8 @@ import {Named} from "./interface/Named.sol"; import {Versioned} from "./interface/Versioned.sol"; import {ServiceAgreementStructsV1} from "./structs/ServiceAgreementStructsV1.sol"; import {GeneralErrors} from "./errors/GeneralErrors.sol"; +import {TokenErrors} from "./errors/TokenErrors.sol"; +import {ServiceAgreementErrorsV1} from "./errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV1U1} from "./errors/ServiceAgreementErrorsV1U1.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -78,10 +80,10 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { function createServiceAgreement( ServiceAgreementStructsV1.ServiceAgreementInputArgs calldata args ) external onlyContracts { - if (args.epochsNumber == 0) revert ServiceAgreementErrorsV1U1.ZeroEpochsNumber(); - if (args.tokenAmount == 0) revert ServiceAgreementErrorsV1U1.ZeroTokenAmount(); + if (args.epochsNumber == 0) revert ServiceAgreementErrorsV1.ZeroEpochsNumber(); + if (args.tokenAmount == 0) revert ServiceAgreementErrorsV1.ZeroTokenAmount(); if (!scoringProxy.isScoreFunction(args.scoreFunctionId)) - revert ServiceAgreementErrorsV1U1.ScoreFunctionDoesntExist(args.scoreFunctionId); + revert ServiceAgreementErrorsV1.ScoreFunctionDoesntExist(args.scoreFunctionId); bytes32 agreementId = hashingProxy.callHashFunction( args.hashFunctionId, @@ -106,9 +108,9 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { IERC20 tknc = tokenContract; if (tknc.allowance(args.assetCreator, address(this)) < args.tokenAmount) - revert ServiceAgreementErrorsV1U1.TooLowAllowance(tknc.allowance(args.assetCreator, address(this))); + revert TokenErrors.TooLowAllowance(address(tknc), tknc.allowance(args.assetCreator, address(this))); if (tknc.balanceOf(args.assetCreator) < args.tokenAmount) - revert ServiceAgreementErrorsV1U1.TooLowBalance(tknc.balanceOf(args.assetCreator)); + revert TokenErrors.TooLowBalance(address(tknc), tknc.balanceOf(args.assetCreator)); tknc.transferFrom(args.assetCreator, sasProxy.agreementV1StorageAddress(), args.tokenAmount); @@ -142,18 +144,16 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { uint16 epochsNumber, uint96 tokenAmount ) external onlyContracts { - if (epochsNumber == 0) revert ServiceAgreementErrorsV1U1.ZeroEpochsNumber(); + if (epochsNumber == 0) revert ServiceAgreementErrorsV1.ZeroEpochsNumber(); ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; sasProxy.setAgreementEpochsNumber(agreementId, sasProxy.getAgreementEpochsNumber(agreementId) + epochsNumber); sasProxy.setAgreementTokenAmount(agreementId, sasProxy.getAgreementTokenAmount(agreementId) + tokenAmount); - if (sasProxy.agreementV1Exists(agreementId)) { + if (sasProxy.agreementV1Exists(agreementId)) _addTokens(assetOwner, sasProxy.agreementV1StorageAddress(), tokenAmount); - } else { - _addTokens(assetOwner, sasProxy.agreementV1U1StorageAddress(), tokenAmount); - } + else _addTokens(assetOwner, sasProxy.agreementV1U1StorageAddress(), tokenAmount); emit ServiceAgreementV1Extended(agreementId, epochsNumber); } @@ -163,11 +163,9 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { sasProxy.setAgreementTokenAmount(agreementId, sasProxy.getAgreementTokenAmount(agreementId) + tokenAmount); - if (sasProxy.agreementV1Exists(agreementId)) { + if (sasProxy.agreementV1Exists(agreementId)) _addTokens(assetOwner, sasProxy.agreementV1StorageAddress(), tokenAmount); - } else { - _addTokens(assetOwner, sasProxy.agreementV1U1StorageAddress(), tokenAmount); - } + else _addTokens(assetOwner, sasProxy.agreementV1U1StorageAddress(), tokenAmount); emit ServiceAgreementV1RewardRaised(agreementId, tokenAmount); } @@ -186,22 +184,18 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { } function isCommitWindowOpen(bytes32 agreementId, uint16 epoch) public view returns (bool) { - if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) { + if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) return commitManagerV1.isCommitWindowOpen(agreementId, epoch); - } else { - return commitManagerV1U1.isCommitWindowOpen(agreementId, epoch); - } + else return commitManagerV1U1.isCommitWindowOpen(agreementId, epoch); } function getTopCommitSubmissions( bytes32 agreementId, uint16 epoch ) external view returns (ServiceAgreementStructsV1.CommitSubmission[] memory) { - if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) { + if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) return commitManagerV1.getTopCommitSubmissions(agreementId, epoch); - } else { - return commitManagerV1U1.getTopCommitSubmissions(agreementId, epoch, 0); - } + else return commitManagerV1U1.getTopCommitSubmissions(agreementId, epoch, 0); } function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { @@ -210,19 +204,14 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { abi.encodePacked(args.assetContract, args.tokenId, args.keyword) ); - if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) { - commitManagerV1.submitCommit(args); - } else { - commitManagerV1U1.submitCommit(args); - } + if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) commitManagerV1.submitCommit(args); + else commitManagerV1U1.submitCommit(args); } function isProofWindowOpen(bytes32 agreementId, uint16 epoch) public view returns (bool) { - if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) { + if (serviceAgreementStorageProxy.agreementV1Exists(agreementId)) return proofManagerV1.isProofWindowOpen(agreementId, epoch); - } else { - return proofManagerV1U1.isProofWindowOpen(agreementId, epoch); - } + else return proofManagerV1U1.isProofWindowOpen(agreementId, epoch); } function getChallenge( @@ -248,14 +237,14 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { } function _addTokens(address assetOwner, address sasAddress, uint96 tokenAmount) internal virtual { - if (tokenAmount == 0) revert ServiceAgreementErrorsV1U1.ZeroTokenAmount(); + if (tokenAmount == 0) revert ServiceAgreementErrorsV1.ZeroTokenAmount(); IERC20 tknc = tokenContract; if (tknc.allowance(assetOwner, address(this)) < tokenAmount) - revert ServiceAgreementErrorsV1U1.TooLowAllowance(tknc.allowance(assetOwner, address(this))); + revert TokenErrors.TooLowAllowance(address(tknc), tknc.allowance(assetOwner, address(this))); if (tknc.balanceOf(assetOwner) < tokenAmount) - revert ServiceAgreementErrorsV1U1.TooLowBalance(tknc.balanceOf(assetOwner)); + revert TokenErrors.TooLowBalance(address(tknc), tknc.balanceOf(assetOwner)); tknc.transferFrom(assetOwner, sasAddress, tokenAmount); } diff --git a/contracts/v1/ShardingTable.sol b/contracts/v1/ShardingTable.sol index 4d7f7936..bcc56127 100644 --- a/contracts/v1/ShardingTable.sol +++ b/contracts/v1/ShardingTable.sol @@ -9,7 +9,7 @@ import {ContractStatus} from "./abstract/ContractStatus.sol"; import {Initializable} from "./interface/Initializable.sol"; import {Named} from "./interface/Named.sol"; import {Versioned} from "./interface/Versioned.sol"; -import {ShardingTableStructs} from "./structs/ShardingTableStructs.sol"; +import {ShardingTableStructsV1} from "./structs/ShardingTableStructsV1.sol"; import {NULL} from "./constants/ShardingTableConstants.sol"; contract ShardingTable is Named, Versioned, ContractStatus, Initializable { @@ -43,11 +43,11 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { function getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) external view returns (ShardingTableStructs.NodeInfo[] memory) { + ) external view returns (ShardingTableStructsV1.NodeInfo[] memory) { return _getShardingTable(startingIdentityId, nodesNumber); } - function getShardingTable() external view returns (ShardingTableStructs.NodeInfo[] memory) { + function getShardingTable() external view returns (ShardingTableStructsV1.NodeInfo[] memory) { ShardingTableStorage sts = shardingTableStorage; return _getShardingTable(sts.head(), sts.nodesCount()); } @@ -106,7 +106,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStorage sts = shardingTableStorage; - ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); + ShardingTableStructsV1.Node memory nodeToRemove = sts.getNode(identityId); uint72 head = sts.head(); uint72 tail = sts.tail(); @@ -120,9 +120,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { } else if (head == identityId) { sts.setHead(nodeToRemove.nextIdentityId); sts.setPrevIdentityId(head, NULL); - } else { - sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); - } + } else sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); sts.deleteNodeObject(identityId); sts.decrementNodesCount(); @@ -133,24 +131,24 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { function _getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) internal view virtual returns (ShardingTableStructs.NodeInfo[] memory) { - ShardingTableStructs.NodeInfo[] memory nodesPage; + ) internal view virtual returns (ShardingTableStructsV1.NodeInfo[] memory) { + ShardingTableStructsV1.NodeInfo[] memory nodesPage; ShardingTableStorage sts = shardingTableStorage; if ((sts.nodesCount() == 0) || (nodesNumber == 0)) { return nodesPage; } - ShardingTableStructs.Node memory startingNode = sts.getNode(startingIdentityId); + ShardingTableStructsV1.Node memory startingNode = sts.getNode(startingIdentityId); require((startingIdentityId == NULL) || (startingNode.identityId != NULL), "Wrong starting Identity ID"); - nodesPage = new ShardingTableStructs.NodeInfo[](nodesNumber); + nodesPage = new ShardingTableStructsV1.NodeInfo[](nodesNumber); ProfileStorage ps = profileStorage; StakingStorage ss = stakingStorage; - nodesPage[0] = ShardingTableStructs.NodeInfo({ + nodesPage[0] = ShardingTableStructsV1.NodeInfo({ nodeId: ps.getNodeId(startingIdentityId), identityId: startingIdentityId, ask: ps.getAsk(startingNode.identityId), @@ -162,7 +160,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { while ((i < nodesNumber) && (nextIdentityId != NULL)) { nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; - nodesPage[i] = ShardingTableStructs.NodeInfo({ + nodesPage[i] = ShardingTableStructsV1.NodeInfo({ nodeId: ps.getNodeId(nextIdentityId), identityId: nextIdentityId, ask: ps.getAsk(nextIdentityId), diff --git a/contracts/v1/Staking.sol b/contracts/v1/Staking.sol index 1b397083..4e2bd863 100644 --- a/contracts/v1/Staking.sol +++ b/contracts/v1/Staking.sol @@ -115,9 +115,8 @@ contract Staking is Named, Versioned, ContractStatus, Initializable { ss.setTotalStake(identityId, newStake); sharesContract.burnFrom(msg.sender, sharesToBurn); - if (shardingTableStorage.nodeExists(identityId) && (newStake < params.minimumStake())) { + if (shardingTableStorage.nodeExists(identityId) && (newStake < params.minimumStake())) shardingTableContract.removeNode(identityId); - } emit StakeWithdrawalStarted( identityId, @@ -170,9 +169,8 @@ contract Staking is Named, Versioned, ContractStatus, Initializable { ss.setTotalStake(identityId, oldStake + delegatorsReward); sasProxy.transferAgreementTokens(agreementId, address(ss), delegatorsReward); - if (!shardingTableStorage.nodeExists(identityId) && oldStake >= parametersStorage.minimumStake()) { + if (!shardingTableStorage.nodeExists(identityId) && oldStake >= parametersStorage.minimumStake()) shardingTableContract.pushBack(identityId); - } } emit AccumulatedOperatorFeeIncreased( @@ -183,11 +181,8 @@ contract Staking is Named, Versioned, ContractStatus, Initializable { ); address sasAddress; - if (sasProxy.agreementV1Exists(agreementId)) { - sasAddress = sasProxy.agreementV1StorageAddress(); - } else { - sasAddress = sasProxy.agreementV1U1StorageAddress(); - } + if (sasProxy.agreementV1Exists(agreementId)) sasAddress = sasProxy.agreementV1StorageAddress(); + else sasAddress = sasProxy.agreementV1U1StorageAddress(); emit StakeIncreased(identityId, ps.getNodeId(identityId), sasAddress, oldStake, oldStake + delegatorsReward); } @@ -220,19 +215,16 @@ contract Staking is Named, Versioned, ContractStatus, Initializable { Shares sharesContract = Shares(ps.getSharesContractAddress(identityId)); uint256 sharesMinted; - if (sharesContract.totalSupply() == 0) { - sharesMinted = stakeAmount; - } else { - sharesMinted = ((stakeAmount * sharesContract.totalSupply()) / oldStake); - } + if (sharesContract.totalSupply() == 0) sharesMinted = stakeAmount; + else sharesMinted = ((stakeAmount * sharesContract.totalSupply()) / oldStake); + sharesContract.mint(sender, sharesMinted); ss.setTotalStake(identityId, newStake); tknc.transferFrom(sender, address(ss), stakeAmount); - if (!shardingTableStorage.nodeExists(identityId) && newStake >= params.minimumStake()) { + if (!shardingTableStorage.nodeExists(identityId) && newStake >= params.minimumStake()) shardingTableContract.pushBack(identityId); - } emit StakeIncreased(identityId, ps.getNodeId(identityId), sender, oldStake, newStake); } diff --git a/contracts/v1/assets/ContentAsset.sol b/contracts/v1/assets/ContentAsset.sol index 53be5398..7591331e 100644 --- a/contracts/v1/assets/ContentAsset.sol +++ b/contracts/v1/assets/ContentAsset.sol @@ -153,13 +153,11 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { ); uint32 r0 = params.r0(); - if ((timeNow < commitPhaseEnd) && (commitsCount < r0)) { + if ((timeNow < commitPhaseEnd) && (commitsCount < r0)) revert ContentAssetErrors.CommitPhaseOngoing(agreementId); - } else if ((timeNow < epochEnd) && (commitsCount >= r0)) { + else if ((timeNow < epochEnd) && (commitsCount >= r0)) revert ContentAssetErrors.CommitPhaseSucceeded(agreementId); - } else if (timeNow > epochEnd) { - revert ContentAssetErrors.FirstEpochHasAlreadyEnded(agreementId); - } + else if (timeNow > epochEnd) revert ContentAssetErrors.FirstEpochHasAlreadyEnded(agreementId); uint96 tokenAmount = sasProxy.getAgreementTokenAmount(agreementId); @@ -210,7 +208,7 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { uss.setUnfinalizedState(tokenId, assertionId); uss.setIssuer(tokenId, msg.sender); - if (!sasProxy.agreementV1U1Exists(agreementId)) { + if (!sasProxy.agreementV1U1Exists(agreementId)) sasProxy.createV1U1ServiceAgreementObject( agreementId, startTime, @@ -220,7 +218,6 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { scoreFunctionIdAndProofWindowOffsetPerc[0], scoreFunctionIdAndProofWindowOffsetPerc[1] ); - } if (updateTokenAmount != 0) serviceAgreementV1.addUpdateTokens(msg.sender, agreementId, updateTokenAmount); @@ -257,25 +254,22 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { bytes32 unfinalizedState = uss.getUnfinalizedState(tokenId); uint256 unfinalizedStateIndex = cas.getAssertionIdsLength(tokenId); - if (unfinalizedState == bytes32(0)) { + if (unfinalizedState == bytes32(0)) revert ContentAssetErrors.NoPendingUpdate(contentAssetStorageAddress, tokenId); - } else if ( + else if ( block.timestamp <= sasProxy.getUpdateCommitsDeadline(keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex))) - ) { + ) revert ContentAssetErrors.PendingUpdateFinalization( contentAssetStorageAddress, tokenId, unfinalizedStateIndex ); - } uint96 updateTokenAmount = sasProxy.getAgreementUpdateTokenAmount(agreementId); - if (sasProxy.agreementV1Exists(agreementId)) { - sasProxy.deleteServiceAgreementV1U1Object(agreementId); - } else { - sasProxy.setAgreementUpdateTokenAmount(agreementId, 0); - } + if (sasProxy.agreementV1Exists(agreementId)) sasProxy.deleteServiceAgreementV1U1Object(agreementId); + else sasProxy.setAgreementUpdateTokenAmount(agreementId, 0); + sasProxy.transferV1U1AgreementTokens(msg.sender, updateTokenAmount); uss.deleteIssuer(tokenId); @@ -316,9 +310,8 @@ contract ContentAsset is Named, Versioned, HubDependent, Initializable { uint128 epochLength; (startTime, oldEpochsNumber, epochLength, , ) = serviceAgreementStorageProxy.getAgreementData(agreementId); - if (block.timestamp > startTime + oldEpochsNumber * epochLength) { + if (block.timestamp > startTime + oldEpochsNumber * epochLength) revert ContentAssetErrors.AssetExpired(tokenId); - } sasV1.extendStoringPeriod(msg.sender, agreementId, epochsNumber, tokenAmount); diff --git a/contracts/v1/errors/GeneralErrors.sol b/contracts/v1/errors/GeneralErrors.sol index ef0b8ec1..a3aae4f0 100644 --- a/contracts/v1/errors/GeneralErrors.sol +++ b/contracts/v1/errors/GeneralErrors.sol @@ -5,4 +5,5 @@ pragma solidity ^0.8.16; library GeneralErrors { error OnlyHubOwnerFunction(address caller); error OnlyHubContractsFunction(address caller); + error OnlyProfileAdminFunction(address caller); } diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1.sol index 0867f502..1b215967 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1.sol @@ -10,8 +10,6 @@ library ServiceAgreementErrorsV1 { error ZeroEpochsNumber(); error ZeroTokenAmount(); error ScoreFunctionDoesntExist(uint8 scoreFunctionId); - error TooLowAllowance(uint256 amount); - error TooLowBalance(uint256 amount); error ServiceAgreementHasBeenExpired( bytes32 agreementId, uint256 startTime, @@ -26,6 +24,7 @@ library ServiceAgreementErrorsV1 { uint256 timeNow ); error NodeNotInShardingTable(uint72 identityId, bytes nodeId, uint96 ask, uint96 stake); + error InvalidScoreFunctionId(bytes32 agreementId, uint16 epoch, uint8 agreementScoreFunctionId, uint256 timeNow); error ProofWindowClosed( bytes32 agreementId, uint16 epoch, diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol index 32c93d30..e4764d8f 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol @@ -3,22 +3,7 @@ pragma solidity ^0.8.16; library ServiceAgreementErrorsV1U1 { - error ServiceAgreementDoesntExist(bytes32 agreementId); - error EmptyAssetCreatorAddress(); - error AssetStorageNotInTheHub(address contractAddress); - error EmptyKeyword(); - error ZeroEpochsNumber(); - error ZeroTokenAmount(); - error ScoreFunctionDoesntExist(uint8 scoreFunctionId); error HashFunctionDoesntExist(uint8 hashFunctionId); - error TooLowAllowance(uint256 amount); - error TooLowBalance(uint256 amount); - error ServiceAgreementHasBeenExpired( - bytes32 agreementId, - uint256 startTime, - uint16 epochsNumber, - uint128 epochLength - ); error CommitWindowClosed( bytes32 agreementId, uint16 epoch, @@ -27,7 +12,6 @@ library ServiceAgreementErrorsV1U1 { uint256 commitWindowClose, uint256 timeNow ); - error NodeNotInShardingTable(uint72 identityId, bytes nodeId, uint96 ask, uint96 stake); error ProofWindowClosed( bytes32 agreementId, uint16 epoch, diff --git a/contracts/v1/errors/TokenErrors.sol b/contracts/v1/errors/TokenErrors.sol new file mode 100644 index 00000000..3e10406c --- /dev/null +++ b/contracts/v1/errors/TokenErrors.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library TokenErrors { + error TooLowAllowance(address tokenAddress, uint256 amount); + error TooLowBalance(address tokenAddress, uint256 amount); +} diff --git a/contracts/v1/scoring/log2pldsf.sol b/contracts/v1/scoring/log2pldsf.sol index d014a417..5efe9dff 100644 --- a/contracts/v1/scoring/log2pldsf.sol +++ b/contracts/v1/scoring/log2pldsf.sol @@ -63,10 +63,13 @@ contract Log2PLDSF is IScoreFunction, Indexable, Named, HubDependent, Initializa } function calculateScore(uint256 distance, uint96 stake) external view returns (uint40) { - uint256 mappedDistance = distance / distanceMappingCoefficient; - uint96 mappedStake = stake / (parametersStorage.maximumStake() / stakeRangeMax); + uint64 coefficient = 1e18; + uint96 maxStake = parametersStorage.maximumStake(); + + uint96 balancedStake = stake <= maxStake ? stake : maxStake; + uint96 mappedStake = balancedStake / (maxStake / stakeRangeMax); - uint64 coefficient = 1 ether; + uint256 mappedDistance = distance / distanceMappingCoefficient; return uint40( diff --git a/contracts/v1/storage/ParametersStorage.sol b/contracts/v1/storage/ParametersStorage.sol index 8eca8643..a68fa2fc 100644 --- a/contracts/v1/storage/ParametersStorage.sol +++ b/contracts/v1/storage/ParametersStorage.sol @@ -10,7 +10,7 @@ contract ParametersStorage is Named, Versioned, HubDependent { event ParameterChanged(string parameterName, uint256 parameterValue); string private constant _NAME = "ParametersStorage"; - string private constant _VERSION = "1.1.0"; + string private constant _VERSION = "1.1.1"; // 0 - minProofWindowOffsetPerc // 1 - maxProofWindowOffsetPerc @@ -42,7 +42,7 @@ contract ParametersStorage is Named, Versioned, HubDependent { // minimumStake args3[0] = 50_000 ether; // maximumStake - args3[1] = 5_000_000 ether; + args3[1] = 2_000_000 ether; r2 = 20; // r1 @@ -64,9 +64,9 @@ contract ParametersStorage is Named, Versioned, HubDependent { epochLength = 90 days; // stakeWithdrawalDelay - args4[0] = 5 minutes; + args4[0] = 28 days; // rewardWithdrawalDelay - args4[1] = 5 minutes; + args4[1] = 28 days; // slashingFreezeDuration args2[2] = 730 days; diff --git a/contracts/v1/storage/ServiceAgreementStorageProxy.sol b/contracts/v1/storage/ServiceAgreementStorageProxy.sol index cc216ea1..fbed9867 100644 --- a/contracts/v1/storage/ServiceAgreementStorageProxy.sol +++ b/contracts/v1/storage/ServiceAgreementStorageProxy.sol @@ -93,11 +93,8 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial } function deleteServiceAgreementObject(bytes32 agreementId) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.deleteServiceAgreementObject(agreementId); - } else { - storageV1U1.deleteServiceAgreementObject(agreementId); - } + if (this.agreementV1Exists(agreementId)) storageV1.deleteServiceAgreementObject(agreementId); + else storageV1U1.deleteServiceAgreementObject(agreementId); } function getAgreementData( @@ -124,73 +121,47 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial [tokenAmount, storageV1U1.getAgreementUpdateTokenAmount(agreementId)], scoreFunctionIdAndProofWindowOffsetPerc ); - } else { - return storageV1U1.getAgreementData(agreementId); - } + } else return storageV1U1.getAgreementData(agreementId); } function getAgreementStartTime(bytes32 agreementId) external view returns (uint256) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementStartTime(agreementId); - } else { - return storageV1U1.getAgreementStartTime(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementStartTime(agreementId); + else return storageV1U1.getAgreementStartTime(agreementId); } function setAgreementStartTime(bytes32 agreementId, uint256 startTime) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementStartTime(agreementId, startTime); - } else { - storageV1U1.setAgreementStartTime(agreementId, startTime); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementStartTime(agreementId, startTime); + else storageV1U1.setAgreementStartTime(agreementId, startTime); } function getAgreementEpochsNumber(bytes32 agreementId) external view returns (uint16) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementEpochsNumber(agreementId); - } else { - return storageV1U1.getAgreementEpochsNumber(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementEpochsNumber(agreementId); + else return storageV1U1.getAgreementEpochsNumber(agreementId); } function setAgreementEpochsNumber(bytes32 agreementId, uint16 epochsNumber) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementEpochsNumber(agreementId, epochsNumber); - } else { - storageV1U1.setAgreementEpochsNumber(agreementId, epochsNumber); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementEpochsNumber(agreementId, epochsNumber); + else storageV1U1.setAgreementEpochsNumber(agreementId, epochsNumber); } function getAgreementEpochLength(bytes32 agreementId) external view returns (uint128) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementEpochLength(agreementId); - } else { - return storageV1U1.getAgreementEpochLength(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementEpochLength(agreementId); + else return storageV1U1.getAgreementEpochLength(agreementId); } function setAgreementEpochLength(bytes32 agreementId, uint128 epochLength) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementEpochLength(agreementId, epochLength); - } else { - storageV1U1.setAgreementEpochLength(agreementId, epochLength); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementEpochLength(agreementId, epochLength); + else storageV1U1.setAgreementEpochLength(agreementId, epochLength); } function getAgreementTokenAmount(bytes32 agreementId) external view returns (uint96) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementTokenAmount(agreementId); - } else { - return storageV1U1.getAgreementTokenAmount(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementTokenAmount(agreementId); + else return storageV1U1.getAgreementTokenAmount(agreementId); } function setAgreementTokenAmount(bytes32 agreementId, uint96 tokenAmount) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementTokenAmount(agreementId, tokenAmount); - } else { - storageV1U1.setAgreementTokenAmount(agreementId, tokenAmount); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementTokenAmount(agreementId, tokenAmount); + else storageV1U1.setAgreementTokenAmount(agreementId, tokenAmount); } function getAgreementUpdateTokenAmount(bytes32 agreementId) external view returns (uint96) { @@ -202,38 +173,27 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial } function getAgreementScoreFunctionId(bytes32 agreementId) external view returns (uint8) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementScoreFunctionId(agreementId); - } else { - return storageV1U1.getAgreementScoreFunctionId(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementScoreFunctionId(agreementId); + else return storageV1U1.getAgreementScoreFunctionId(agreementId); } function setAgreementScoreFunctionId(bytes32 agreementId, uint8 newScoreFunctionId) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementScoreFunctionId(agreementId, newScoreFunctionId); - } else { - storageV1U1.setAgreementScoreFunctionId(agreementId, newScoreFunctionId); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementScoreFunctionId(agreementId, newScoreFunctionId); + else storageV1U1.setAgreementScoreFunctionId(agreementId, newScoreFunctionId); } function getAgreementProofWindowOffsetPerc(bytes32 agreementId) external view returns (uint8) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementProofWindowOffsetPerc(agreementId); - } else { - return storageV1U1.getAgreementProofWindowOffsetPerc(agreementId); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementProofWindowOffsetPerc(agreementId); + else return storageV1U1.getAgreementProofWindowOffsetPerc(agreementId); } function setAgreementProofWindowOffsetPerc( bytes32 agreementId, uint8 proofWindowOffsetPerc ) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementProofWindowOffsetPerc(agreementId, proofWindowOffsetPerc); - } else { - storageV1U1.setAgreementProofWindowOffsetPerc(agreementId, proofWindowOffsetPerc); - } + else storageV1U1.setAgreementProofWindowOffsetPerc(agreementId, proofWindowOffsetPerc); } function getV1U1AgreementEpochSubmissionHead( @@ -266,27 +226,18 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial } function incrementAgreementRewardedNodesNumber(bytes32 agreementId, uint16 epoch) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.incrementAgreementRewardedNodesNumber(agreementId, epoch); - } else { - storageV1U1.incrementAgreementRewardedNodesNumber(agreementId, epoch); - } + if (this.agreementV1Exists(agreementId)) storageV1.incrementAgreementRewardedNodesNumber(agreementId, epoch); + else storageV1U1.incrementAgreementRewardedNodesNumber(agreementId, epoch); } function decrementAgreementRewardedNodesNumber(bytes32 agreementId, uint16 epoch) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.decrementAgreementRewardedNodesNumber(agreementId, epoch); - } else { - storageV1U1.decrementAgreementRewardedNodesNumber(agreementId, epoch); - } + if (this.agreementV1Exists(agreementId)) storageV1.decrementAgreementRewardedNodesNumber(agreementId, epoch); + else storageV1U1.decrementAgreementRewardedNodesNumber(agreementId, epoch); } function getAgreementRewardedNodesNumber(bytes32 agreementId, uint16 epoch) external view returns (uint32) { - if (this.agreementV1Exists(agreementId)) { - return storageV1.getAgreementRewardedNodesNumber(agreementId, epoch); - } else { - return storageV1U1.getAgreementRewardedNodesNumber(agreementId, epoch); - } + if (this.agreementV1Exists(agreementId)) return storageV1.getAgreementRewardedNodesNumber(agreementId, epoch); + else return storageV1U1.getAgreementRewardedNodesNumber(agreementId, epoch); } function setAgreementRewardedNodesNumber( @@ -294,19 +245,14 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial uint16 epoch, uint32 rewardedNodesNumber ) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementRewardedNodesNumber(agreementId, epoch, rewardedNodesNumber); - } else { - storageV1U1.setAgreementRewardedNodesNumber(agreementId, epoch, rewardedNodesNumber); - } + else storageV1U1.setAgreementRewardedNodesNumber(agreementId, epoch, rewardedNodesNumber); } function deleteAgreementRewardedNodesNumber(bytes32 agreementId, uint16 epoch) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.setAgreementRewardedNodesNumber(agreementId, epoch, 0); - } else { - storageV1U1.deleteAgreementRewardedNodesNumber(agreementId, epoch); - } + if (this.agreementV1Exists(agreementId)) storageV1.setAgreementRewardedNodesNumber(agreementId, epoch, 0); + else storageV1U1.deleteAgreementRewardedNodesNumber(agreementId, epoch); } function createV1CommitSubmissionObject( @@ -330,93 +276,62 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial } function deleteCommitSubmissionsObject(bytes32 commitId) external onlyContracts { - if (this.commitV1U1Exists(commitId)) { - storageV1U1.deleteEpochStateCommitSubmissionsObject(commitId); - } else { - storageV1.deleteCommitSubmissionsObject(commitId); - } + if (this.commitV1U1Exists(commitId)) storageV1U1.deleteEpochStateCommitSubmissionsObject(commitId); + else storageV1.deleteCommitSubmissionsObject(commitId); } function getCommitSubmission( bytes32 commitId ) external view returns (ServiceAgreementStructsV1.CommitSubmission memory) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.getEpochStateCommitSubmission(commitId); - } else { - return storageV1.getCommitSubmission(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.getEpochStateCommitSubmission(commitId); + else return storageV1.getCommitSubmission(commitId); } function getCommitSubmissionIdentityId(bytes32 commitId) external view returns (uint72) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.getEpochStateCommitSubmissionIdentityId(commitId); - } else { - return storageV1.getCommitSubmissionIdentityId(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.getEpochStateCommitSubmissionIdentityId(commitId); + else return storageV1.getCommitSubmissionIdentityId(commitId); } function setCommitSubmissionIdentityId(bytes32 commitId, uint72 identityId) external onlyContracts { - if (this.commitV1U1Exists(commitId)) { - storageV1U1.setEpochStateCommitSubmissionIdentityId(commitId, identityId); - } else { - storageV1.setCommitSubmissionIdentityId(commitId, identityId); - } + if (this.commitV1U1Exists(commitId)) storageV1U1.setEpochStateCommitSubmissionIdentityId(commitId, identityId); + else storageV1.setCommitSubmissionIdentityId(commitId, identityId); } function getCommitSubmissionPrevIdentityId(bytes32 commitId) external view returns (uint72) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.getEpochStateCommitSubmissionPrevIdentityId(commitId); - } else { - return storageV1.getCommitSubmissionPrevIdentityId(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.getEpochStateCommitSubmissionPrevIdentityId(commitId); + else return storageV1.getCommitSubmissionPrevIdentityId(commitId); } function setCommitSubmissionPrevIdentityId(bytes32 commitId, uint72 prevIdentityId) external onlyContracts { - if (this.commitV1U1Exists(commitId)) { + if (this.commitV1U1Exists(commitId)) storageV1U1.setEpochStateCommitSubmissionPrevIdentityId(commitId, prevIdentityId); - } else { - storageV1.setCommitSubmissionPrevIdentityId(commitId, prevIdentityId); - } + else storageV1.setCommitSubmissionPrevIdentityId(commitId, prevIdentityId); } function getCommitSubmissionNextIdentityId(bytes32 commitId) external view returns (uint72) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.getEpochStateCommitSubmissionNextIdentityId(commitId); - } else { - return storageV1.getCommitSubmissionNextIdentityId(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.getEpochStateCommitSubmissionNextIdentityId(commitId); + else return storageV1.getCommitSubmissionNextIdentityId(commitId); } function setCommitSubmissionNextIdentityId(bytes32 commitId, uint72 nextIdentityId) external onlyContracts { - if (this.commitV1U1Exists(commitId)) { + if (this.commitV1U1Exists(commitId)) storageV1U1.setEpochStateCommitSubmissionNextIdentityId(commitId, nextIdentityId); - } else { - storageV1.setCommitSubmissionNextIdentityId(commitId, nextIdentityId); - } + else storageV1.setCommitSubmissionNextIdentityId(commitId, nextIdentityId); } function getCommitSubmissionScore(bytes32 commitId) external view returns (uint40) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.getEpochStateCommitSubmissionScore(commitId); - } else { - return storageV1.getCommitSubmissionScore(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.getEpochStateCommitSubmissionScore(commitId); + else return storageV1.getCommitSubmissionScore(commitId); } function setCommitSubmissionScore(bytes32 commitId, uint40 score) external onlyContracts { - if (this.commitV1U1Exists(commitId)) { - storageV1U1.setEpochStateCommitSubmissionScore(commitId, score); - } else { - storageV1.setCommitSubmissionScore(commitId, score); - } + if (this.commitV1U1Exists(commitId)) storageV1U1.setEpochStateCommitSubmissionScore(commitId, score); + else storageV1.setCommitSubmissionScore(commitId, score); } function commitSubmissionExists(bytes32 commitId) external view returns (bool) { - if (this.commitV1U1Exists(commitId)) { - return storageV1U1.epochStateCommitSubmissionExists(commitId); - } else { - return storageV1.commitSubmissionExists(commitId); - } + if (this.commitV1U1Exists(commitId)) return storageV1U1.epochStateCommitSubmissionExists(commitId); + else return storageV1.commitSubmissionExists(commitId); } function incrementCommitsCount(bytes32 epochStateId) external onlyContracts { @@ -452,11 +367,8 @@ contract ServiceAgreementStorageProxy is Named, Versioned, HubDependent, Initial } function transferAgreementTokens(bytes32 agreementId, address receiver, uint96 tokenAmount) external onlyContracts { - if (this.agreementV1Exists(agreementId)) { - storageV1.transferAgreementTokens(receiver, tokenAmount); - } else { - storageV1U1.transferAgreementTokens(receiver, tokenAmount); - } + if (this.agreementV1Exists(agreementId)) storageV1.transferAgreementTokens(receiver, tokenAmount); + else storageV1U1.transferAgreementTokens(receiver, tokenAmount); } function transferV1AgreementTokens(address receiver, uint96 tokenAmount) external onlyContracts { diff --git a/contracts/v1/storage/ShardingTableStorage.sol b/contracts/v1/storage/ShardingTableStorage.sol index 965628a9..8bfdf659 100644 --- a/contracts/v1/storage/ShardingTableStorage.sol +++ b/contracts/v1/storage/ShardingTableStorage.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.16; import {HubDependent} from "../abstract/HubDependent.sol"; import {Named} from "../interface/Named.sol"; import {Versioned} from "../interface/Versioned.sol"; -import {ShardingTableStructs} from "../structs/ShardingTableStructs.sol"; +import {ShardingTableStructsV1} from "../structs/ShardingTableStructsV1.sol"; import {NULL} from "../constants/ShardingTableConstants.sol"; contract ShardingTableStorage is Named, Versioned, HubDependent { @@ -17,7 +17,7 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { uint72 public nodesCount; // identityId => Node - mapping(uint72 => ShardingTableStructs.Node) internal nodes; + mapping(uint72 => ShardingTableStructsV1.Node) internal nodes; constructor(address hubAddress) HubDependent(hubAddress) { head = NULL; @@ -49,14 +49,14 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { } function createNodeObject(uint72 identityId, uint72 prevIdentityId, uint72 nextIdentityId) external onlyContracts { - nodes[identityId] = ShardingTableStructs.Node({ + nodes[identityId] = ShardingTableStructsV1.Node({ identityId: identityId, prevIdentityId: prevIdentityId, nextIdentityId: nextIdentityId }); } - function getNode(uint72 identityId) external view returns (ShardingTableStructs.Node memory) { + function getNode(uint72 identityId) external view returns (ShardingTableStructsV1.Node memory) { return nodes[identityId]; } @@ -79,10 +79,10 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { function getMultipleNodes( uint72 firstIdentityId, uint16 nodesNumber - ) external view returns (ShardingTableStructs.Node[] memory) { - ShardingTableStructs.Node[] memory nodesPage = new ShardingTableStructs.Node[](nodesNumber); + ) external view returns (ShardingTableStructsV1.Node[] memory) { + ShardingTableStructsV1.Node[] memory nodesPage = new ShardingTableStructsV1.Node[](nodesNumber); - ShardingTableStructs.Node memory currentNode = nodes[firstIdentityId]; + ShardingTableStructsV1.Node memory currentNode = nodes[firstIdentityId]; for (uint256 i; i < nodesNumber; ) { nodesPage[i] = currentNode; currentNode = nodes[currentNode.nextIdentityId]; diff --git a/contracts/v1/structs/ShardingTableStructs.sol b/contracts/v1/structs/ShardingTableStructsV1.sol similarity index 89% rename from contracts/v1/structs/ShardingTableStructs.sol rename to contracts/v1/structs/ShardingTableStructsV1.sol index 654efdb6..53259b79 100644 --- a/contracts/v1/structs/ShardingTableStructs.sol +++ b/contracts/v1/structs/ShardingTableStructsV1.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.16; -library ShardingTableStructs { +library ShardingTableStructsV1 { struct NodeInfo { bytes nodeId; uint72 identityId; diff --git a/contracts/v1/utils/ByteArr.sol b/contracts/v1/utils/ByteArr.sol index 2989ad55..642bed16 100644 --- a/contracts/v1/utils/ByteArr.sol +++ b/contracts/v1/utils/ByteArr.sol @@ -5,9 +5,7 @@ pragma solidity ^0.8.16; library ByteArr { function indexOf(bytes32[] storage self, bytes32 item) internal view returns (uint index, bool isThere) { for (uint i; i < self.length; i++) { - if (self[i] == item) { - return (i, true); - } + if (self[i] == item) return (i, true); } return (0, false); } diff --git a/contracts/v2/CommitManagerV1.sol b/contracts/v2/CommitManagerV1.sol new file mode 100644 index 00000000..93e74558 --- /dev/null +++ b/contracts/v2/CommitManagerV1.sol @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; +import {LinearSum} from "./scoring/LinearSum.sol"; +import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; +import {ParametersStorage} from "../v1/storage/ParametersStorage.sol"; +import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; +import {ServiceAgreementStorageProxy} from "../v1/storage/ServiceAgreementStorageProxy.sol"; +import {ShardingTableStorageV2} from "./storage/ShardingTableStorage.sol"; +import {StakingStorage} from "../v1/storage/StakingStorage.sol"; +import {ContractStatus} from "../v1/abstract/ContractStatus.sol"; +import {Initializable} from "../v1/interface/Initializable.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; +import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1.sol"; +import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; +import {ShardingTableStructsV2} from "../v2/structs/ShardingTableStructsV2.sol"; +import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; +import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; +import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; + +contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { + event CommitSubmitted( + address indexed assetContract, + uint256 indexed tokenId, + bytes keyword, + uint8 hashFunctionId, + uint16 epoch, + uint72 indexed identityId, + uint40 score + ); + + string private constant _NAME = "CommitManagerV1"; + string private constant _VERSION = "2.0.0"; + + uint8 private constant _LOG2PLDSF_ID = 1; + uint8 private constant _LINEAR_SUM_ID = 2; + + bool[4] public reqs = [false, false, false, false]; + + Log2PLDSF public log2pldsf; + LinearSum public linearSum; + IdentityStorageV2 public identityStorage; + ParametersStorage public parametersStorage; + ProfileStorage public profileStorage; + ServiceAgreementStorageProxy public serviceAgreementStorageProxy; + ShardingTableStorageV2 public shardingTableStorage; + StakingStorage public stakingStorage; + + uint256 constant HASH_RING_SIZE = type(uint256).max; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) ContractStatus(hubAddress) {} + + function initialize() public onlyHubOwner { + log2pldsf = Log2PLDSF(hub.getContractAddress("Log2PLDSF")); + linearSum = LinearSum(hub.getContractAddress("LinearSum")); + identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); + parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); + profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); + serviceAgreementStorageProxy = ServiceAgreementStorageProxy( + hub.getContractAddress("ServiceAgreementStorageProxy") + ); + shardingTableStorage = ShardingTableStorageV2(hub.getContractAddress("ShardingTableStorage")); + stakingStorage = StakingStorage(hub.getContractAddress("StakingStorage")); + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function isCommitWindowOpen(bytes32 agreementId, uint16 epoch) public view returns (bool) { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + if (!sasProxy.agreementV1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + uint256 startTime = sasProxy.getAgreementStartTime(agreementId); + + ParametersStorage params = parametersStorage; + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + + if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( + agreementId, + startTime, + sasProxy.getAgreementEpochsNumber(agreementId), + epochLength + ); + + uint256 timeNow = block.timestamp; + uint256 commitWindowDuration = (params.commitWindowDurationPerc() * epochLength) / 100; + + if (epoch == 0) return timeNow < (startTime + commitWindowDuration); + + return (timeNow >= (startTime + epochLength * epoch) && + timeNow < (startTime + epochLength * epoch + commitWindowDuration)); + } + + function getTopCommitSubmissions( + bytes32 agreementId, + uint16 epoch + ) external view returns (ServiceAgreementStructsV1.CommitSubmission[] memory) { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + if (!sasProxy.agreementV1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( + agreementId, + sasProxy.getAgreementStartTime(agreementId), + sasProxy.getAgreementEpochsNumber(agreementId), + sasProxy.getAgreementEpochLength(agreementId) + ); + + uint32 r0 = parametersStorage.r0(); + + ServiceAgreementStructsV1.CommitSubmission[] + memory epochCommits = new ServiceAgreementStructsV1.CommitSubmission[](r0); + + bytes32 epochSubmissionsHead = sasProxy.getV1AgreementEpochSubmissionHead(agreementId, epoch); + + epochCommits[0] = sasProxy.getCommitSubmission(epochSubmissionsHead); + + bytes32 commitId; + uint72 nextIdentityId = epochCommits[0].nextIdentityId; + uint8 submissionsIdx = 1; + while ((submissionsIdx < r0) && (nextIdentityId != 0)) { + commitId = keccak256(abi.encodePacked(agreementId, epoch, nextIdentityId)); + epochCommits[submissionsIdx] = sasProxy.getCommitSubmission(commitId); + + nextIdentityId = epochCommits[submissionsIdx].nextIdentityId; + + unchecked { + submissionsIdx++; + } + } + + return epochCommits; + } + + function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (!sasProxy.agreementV1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); + + revert ServiceAgreementErrorsV1.CommitWindowClosed( + agreementId, + args.epoch, + actualCommitWindowStart, + actualCommitWindowStart + (parametersStorage.commitWindowDurationPerc() * epochLength) / 100, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + Log2PLDSF l2p = log2pldsf; + + uint40 score = l2p.calculateScore( + l2p.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), + stakingStorage.totalStakes(identityId) + ); + + _insertCommit(agreementId, args.epoch, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + identityId, + score + ); + } + + function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (!sasProxy.agreementV1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); + + revert ServiceAgreementErrorsV1.CommitWindowClosed( + agreementId, + args.epoch, + actualCommitWindowStart, + actualCommitWindowStart + (parametersStorage.commitWindowDurationPerc() * epochLength) / 100, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( + agreementId, + args.epoch, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + LinearSum ls = linearSum; + + uint256 distance = ls.calculateDistance( + args.hashFunctionId, + profileStorage.getNodeId(identityId), + args.keyword + ); + + uint40 score = ls.calculateScore(distance, maxDistance, nodesCount, stakingStorage.totalStakes(identityId)); + + _insertCommit(agreementId, args.epoch, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + identityId, + score + ); + } + + function setReq(uint256 index, bool req) external onlyHubOwner { + reqs[index] = req; + } + + function _verifyNeighborhood( + bytes32 agreementId, + uint16 epoch, + uint8 hashFunctionId, + bytes calldata keyword, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex + ) internal virtual returns (uint72, uint256) { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + LinearSum ls = linearSum; + + ( + ShardingTableStructsV2.Node memory leftEdgeNode, + ShardingTableStructsV2.Node memory closestNode, + ShardingTableStructsV2.Node memory rightEdgeNode + ) = sts.getNeighborhoodBoundaryByIndexes(leftEdgeNodeIndex, closestNodeIndex, rightEdgeNodeIndex); + + bool isBetween = (leftEdgeNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + ? (closestNode.hashRingPosition >= leftEdgeNode.hashRingPosition && + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + : (leftEdgeNode.hashRingPosition <= closestNode.hashRingPosition || + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); + + uint72 nodesCount = sts.nodesCount(); + uint72 neighborhoodSize = (leftEdgeNode.index <= rightEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index + 1 + : (nodesCount - leftEdgeNode.index) + rightEdgeNode.index + 1; + + (uint72 closestPrevIdentityId, uint72 closestNextIdentityId) = sts.getAdjacentIdentityIdsByIndex( + closestNodeIndex + ); + uint72 rightEdgeNextIdentityId = rightEdgeNodeIndex != nodesCount - 1 + ? sts.indexToIdentityId(rightEdgeNodeIndex + 1) + : sts.indexToIdentityId(0); + uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 + ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) + : sts.indexToIdentityId(nodesCount - 1); + + (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = ls + .calculateNeighborhoodBoundaryDistances( + hashFunctionId, + leftEdgeNode.nodeId, + closestNode.nodeId, + rightEdgeNode.nodeId, + keyword + ); + + // Verify that closestNode is in smaller arc between leftNode and rightNode + if (!isBetween) + revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( + agreementId, + epoch, + closestNodeIndex, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + block.timestamp + ); + + // Verify number of nodes between leftNode and rightNode (should be R2) + if (neighborhoodSize != parametersStorage.r2()) + revert CommitManagerErrorsV2.InvalidNeighborhoodSize( + agreementId, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + nodesCount, + parametersStorage.r2(), + neighborhoodSize, + block.timestamp + ); + + // Verify that closestNode is indeed closest + if ( + closestDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(closestPrevIdentityId), keyword) || + closestDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(closestNextIdentityId), keyword) + ) + revert CommitManagerErrorsV2.InvalidClosestNode( + agreementId, + epoch, + closestNodeIndex, + closestDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(closestPrevIdentityId), keyword), + ls.calculateDistance(hashFunctionId, ps.getNodeId(closestNextIdentityId), keyword), + block.timestamp + ); + + // Verify that leftNode is indeed the left edge of the Neighborhood + if (leftEdgeDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(rightEdgeNextIdentityId), keyword)) + revert CommitManagerErrorsV2.InvalidLeftEdgeNode( + agreementId, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex != nodesCount - 1 ? rightEdgeNodeIndex + 1 : 0, + leftEdgeDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(rightEdgeNextIdentityId), keyword), + block.timestamp + ); + + // Verify that rightNode is indeed the right edge of the Neighborhood + if (rightEdgeDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword)) + revert CommitManagerErrorsV2.InvalidRightEdgeNode( + agreementId, + epoch, + rightEdgeNodeIndex, + leftEdgeNodeIndex != 0 ? leftEdgeNodeIndex - 1 : nodesCount - 1, + rightEdgeDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword), + block.timestamp + ); + + return (nodesCount, (leftEdgeDistance > rightEdgeDistance) ? leftEdgeDistance : rightEdgeDistance); + } + + function _insertCommit( + bytes32 agreementId, + uint16 epoch, + uint72 identityId, + uint72 prevIdentityId, + uint72 nextIdentityId, + uint40 score + ) internal virtual { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 commitId = keccak256(abi.encodePacked(agreementId, epoch, identityId)); + + if (!reqs[2] && sasProxy.commitSubmissionExists(commitId)) + revert ServiceAgreementErrorsV1.NodeAlreadySubmittedCommit( + agreementId, + epoch, + identityId, + profileStorage.getNodeId(identityId) + ); + + bytes32 refCommitId = sasProxy.getV1AgreementEpochSubmissionHead(agreementId, epoch); + + ParametersStorage params = parametersStorage; + + uint72 refCommitNextIdentityId = sasProxy.getCommitSubmissionNextIdentityId(refCommitId); + uint32 r0 = params.r0(); + uint8 i; + while ((score < sasProxy.getCommitSubmissionScore(refCommitId)) && (refCommitNextIdentityId != 0) && (i < r0)) { + refCommitId = keccak256(abi.encodePacked(agreementId, epoch, refCommitNextIdentityId)); + + refCommitNextIdentityId = sasProxy.getCommitSubmissionNextIdentityId(refCommitId); + unchecked { + i++; + } + } + + if (!reqs[3] && (i >= r0)) + revert ServiceAgreementErrorsV1.NodeNotAwarded( + agreementId, + epoch, + identityId, + profileStorage.getNodeId(identityId), + i + ); + + sasProxy.createV1CommitSubmissionObject(commitId, identityId, prevIdentityId, nextIdentityId, score); + + ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); + + if ((i == 0) && (refCommit.identityId == 0)) + // No head -> Setting new head + sasProxy.setV1AgreementEpochSubmissionHead(agreementId, epoch, commitId); + else if ((i == 0) && (score <= refCommit.score)) + // There is a head with higher or equal score, add new commit on the right + _linkCommits(agreementId, epoch, refCommit.identityId, identityId); + else if ((i == 0) && (score > refCommit.score)) { + // There is a head with lower score, replace the head + sasProxy.setV1AgreementEpochSubmissionHead(agreementId, epoch, commitId); + _linkCommits(agreementId, epoch, identityId, refCommit.identityId); + } else if (score > refCommit.score) { + // [H] - head + // [RC] - reference commit + // [RC-] - commit before reference commit + // [RC+] - commit after reference commit + // [NC] - new commit + // [] <-> [H] <-> [X] ... [RC-] <-> [RC] <-> [RC+] ... [C] <-> [] + // [] <-> [H] <-> [X] ... [RC-] <-(NL)-> [NC] <-(NL)-> [RC] <-> [RC+] ... [C] <-> [] + _linkCommits(agreementId, epoch, refCommit.prevIdentityId, identityId); + _linkCommits(agreementId, epoch, identityId, refCommit.identityId); + } + // [] <-> [H] <-> [RC] <-> [] + // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] + else _linkCommits(agreementId, epoch, refCommit.identityId, identityId); + } + + function _linkCommits( + bytes32 agreementId, + uint16 epoch, + uint72 leftIdentityId, + uint72 rightIdentityId + ) internal virtual { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + sasProxy.setCommitSubmissionNextIdentityId( + keccak256(abi.encodePacked(agreementId, epoch, leftIdentityId)), // leftCommitId + rightIdentityId + ); + + sasProxy.setCommitSubmissionPrevIdentityId( + keccak256(abi.encodePacked(agreementId, epoch, rightIdentityId)), // rightCommitId + leftIdentityId + ); + } +} diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol new file mode 100644 index 00000000..99ca4be0 --- /dev/null +++ b/contracts/v2/CommitManagerV1U1.sol @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; +import {LinearSum} from "./scoring/LinearSum.sol"; +import {ContentAssetStorage} from "../v1/storage/assets/ContentAssetStorage.sol"; +import {ContentAssetStorageV2} from "./storage/assets/ContentAssetStorage.sol"; +import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; +import {ParametersStorage} from "../v1/storage/ParametersStorage.sol"; +import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; +import {ServiceAgreementStorageProxy} from "../v1/storage/ServiceAgreementStorageProxy.sol"; +import {ShardingTableStorageV2} from "./storage/ShardingTableStorage.sol"; +import {StakingStorage} from "../v1/storage/StakingStorage.sol"; +import {UnfinalizedStateStorage} from "../v1/storage/UnfinalizedStateStorage.sol"; +import {AbstractAsset} from "../v1/abstract/AbstractAsset.sol"; +import {ContractStatus} from "../v1/abstract/ContractStatus.sol"; +import {Initializable} from "../v1/interface/Initializable.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; +import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1.sol"; +import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; +import {ShardingTableStructsV2} from "./structs/ShardingTableStructsV2.sol"; +import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; +import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; +import {ServiceAgreementErrorsV1U1} from "../v1/errors/ServiceAgreementErrorsV1U1.sol"; +import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; + +contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { + event CommitSubmitted( + address assetContract, + uint256 tokenId, + bytes keyword, + uint8 hashFunctionId, + uint16 epoch, + uint256 stateIndex, + uint72 identityId, + uint40 score + ); + event StateFinalized( + address assetContract, + uint256 tokenId, + bytes keyword, + uint8 hashFunctionId, + uint16 epoch, + uint256 stateIndex, + bytes32 state + ); + + string private constant _NAME = "CommitManagerV1U1"; + string private constant _VERSION = "2.0.0"; + + uint8 private constant _LOG2PLDSF_ID = 1; + uint8 private constant _LINEAR_SUM_ID = 2; + + bool[6] public reqs = [false, false, false, false, false, false]; + + Log2PLDSF public log2pldsf; + LinearSum public linearSum; + ContentAssetStorageV2 public contentAssetStorage; + IdentityStorageV2 public identityStorage; + ParametersStorage public parametersStorage; + ProfileStorage public profileStorage; + ServiceAgreementStorageProxy public serviceAgreementStorageProxy; + ShardingTableStorageV2 public shardingTableStorage; + StakingStorage public stakingStorage; + UnfinalizedStateStorage public unfinalizedStateStorage; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) ContractStatus(hubAddress) {} + + function initialize() public onlyHubOwner { + log2pldsf = Log2PLDSF(hub.getContractAddress("Log2PLDSF")); + linearSum = LinearSum(hub.getContractAddress("LinearSum")); + contentAssetStorage = ContentAssetStorageV2(hub.getAssetStorageAddress("ContentAssetStorage")); + identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); + parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); + profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); + serviceAgreementStorageProxy = ServiceAgreementStorageProxy( + hub.getContractAddress("ServiceAgreementStorageProxy") + ); + shardingTableStorage = ShardingTableStorageV2(hub.getContractAddress("ShardingTableStorage")); + stakingStorage = StakingStorage(hub.getContractAddress("StakingStorage")); + unfinalizedStateStorage = UnfinalizedStateStorage(hub.getContractAddress("UnfinalizedStateStorage")); + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function isCommitWindowOpen(bytes32 agreementId, uint16 epoch) public view returns (bool) { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + uint256 startTime = sasProxy.getAgreementStartTime(agreementId); + + ParametersStorage params = parametersStorage; + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + + if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( + agreementId, + startTime, + sasProxy.getAgreementEpochsNumber(agreementId), + epochLength + ); + + uint256 timeNow = block.timestamp; + uint256 commitWindowDuration = (params.commitWindowDurationPerc() * epochLength) / 100; + + if (epoch == 0) return timeNow < (startTime + commitWindowDuration); + + return (timeNow >= (startTime + epochLength * epoch) && + timeNow < (startTime + epochLength * epoch + commitWindowDuration)); + } + + function isUpdateCommitWindowOpen( + bytes32 agreementId, + uint16 epoch, + uint256 stateIndex + ) public view returns (bool) { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + + if (!sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( + agreementId, + sasProxy.getAgreementStartTime(agreementId), + sasProxy.getAgreementEpochsNumber(agreementId), + epochLength + ); + + uint256 commitWindowEnd = sasProxy.getUpdateCommitsDeadline( + keccak256(abi.encodePacked(agreementId, stateIndex)) + ); + + return block.timestamp < commitWindowEnd; + } + + function getTopCommitSubmissions( + bytes32 agreementId, + uint16 epoch, + uint256 stateIndex + ) external view returns (ServiceAgreementStructsV1.CommitSubmission[] memory) { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + if (epoch >= sasProxy.getAgreementEpochsNumber(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementHasBeenExpired( + agreementId, + sasProxy.getAgreementStartTime(agreementId), + sasProxy.getAgreementEpochsNumber(agreementId), + sasProxy.getAgreementEpochLength(agreementId) + ); + + uint32 r0 = parametersStorage.r0(); + + ServiceAgreementStructsV1.CommitSubmission[] + memory epochStateCommits = new ServiceAgreementStructsV1.CommitSubmission[](r0); + + bytes32 epochSubmissionsHead = sasProxy.getV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex); + + epochStateCommits[0] = sasProxy.getCommitSubmission(epochSubmissionsHead); + + bytes32 commitId; + uint72 nextIdentityId = epochStateCommits[0].nextIdentityId; + uint8 submissionsIdx = 1; + while ((submissionsIdx < r0) && (nextIdentityId != 0)) { + commitId = keccak256(abi.encodePacked(agreementId, epoch, stateIndex, nextIdentityId)); + epochStateCommits[submissionsIdx] = sasProxy.getCommitSubmission(commitId); + + nextIdentityId = epochStateCommits[submissionsIdx].nextIdentityId; + + unchecked { + submissionsIdx++; + } + } + + return epochStateCommits; + } + + function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + Log2PLDSF l2p = log2pldsf; + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + uint256 latestFinalizedStateIndex = AbstractAsset(args.assetContract).getAssertionIdsLength(args.tokenId) - 1; + + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); + + revert ServiceAgreementErrorsV1U1.CommitWindowClosed( + agreementId, + args.epoch, + latestFinalizedStateIndex, + actualCommitWindowStart, + actualCommitWindowStart + (parametersStorage.commitWindowDurationPerc() * epochLength) / 100, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + uint40 score = l2p.calculateScore( + l2p.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), + stakingStorage.totalStakes(identityId) + ); + + _insertCommit(agreementId, args.epoch, latestFinalizedStateIndex, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + latestFinalizedStateIndex, + identityId, + score + ); + } + + function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + LinearSum ls = linearSum; + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + uint256 latestFinalizedStateIndex = AbstractAsset(args.assetContract).getAssertionIdsLength(args.tokenId) - 1; + + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { + uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); + uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); + + revert ServiceAgreementErrorsV1U1.CommitWindowClosed( + agreementId, + args.epoch, + latestFinalizedStateIndex, + actualCommitWindowStart, + actualCommitWindowStart + (parametersStorage.commitWindowDurationPerc() * epochLength) / 100, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( + agreementId, + args.epoch, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + uint40 score = ls.calculateScore( + ls.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), + maxDistance, + nodesCount, + stakingStorage.totalStakes(identityId) + ); + + _insertCommit(agreementId, args.epoch, latestFinalizedStateIndex, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + latestFinalizedStateIndex, + identityId, + score + ); + } + + function submitUpdateCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + UnfinalizedStateStorage uss = unfinalizedStateStorage; + AbstractAsset generalAssetInterface = AbstractAsset(args.assetContract); + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + Log2PLDSF l2p = log2pldsf; + + bytes32 unfinalizedState = uss.getUnfinalizedState(args.tokenId); + uint256 unfinalizedStateIndex = generalAssetInterface.getAssertionIdsLength(args.tokenId); + + if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) + revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (!sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV1.InvalidScoreFunctionId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + if (!reqs[2] && !isUpdateCommitWindowOpen(agreementId, args.epoch, unfinalizedStateIndex)) { + uint256 commitWindowEnd = sasProxy.getUpdateCommitsDeadline( + keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex)) + ); + + revert ServiceAgreementErrorsV1U1.CommitWindowClosed( + agreementId, + args.epoch, + unfinalizedStateIndex, + commitWindowEnd - parametersStorage.updateCommitWindowDuration(), + commitWindowEnd, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[3] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + uint40 score = l2p.calculateScore( + l2p.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), + stakingStorage.totalStakes(identityId) + ); + + _insertCommit(agreementId, args.epoch, unfinalizedStateIndex, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + unfinalizedStateIndex, + identityId, + score + ); + + if ( + sasProxy.getCommitsCount(keccak256(abi.encodePacked(agreementId, args.epoch, unfinalizedStateIndex))) == + parametersStorage.finalizationCommitsNumber() + ) { + if (sasProxy.agreementV1Exists(agreementId)) sasProxy.migrateV1ServiceAgreement(agreementId); + + sasProxy.setAgreementTokenAmount( + agreementId, + sasProxy.getAgreementTokenAmount(agreementId) + sasProxy.getAgreementUpdateTokenAmount(agreementId) + ); + sasProxy.setAgreementUpdateTokenAmount(agreementId, 0); + + ContentAssetStorage cas = contentAssetStorage; + cas.setAssertionIssuer(args.tokenId, unfinalizedState, uss.getIssuer(args.tokenId)); + cas.pushAssertionId(args.tokenId, unfinalizedState); + + uss.deleteIssuer(args.tokenId); + uss.deleteUnfinalizedState(args.tokenId); + + emit StateFinalized( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + unfinalizedStateIndex, + unfinalizedState + ); + } + } + + function submitUpdateCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + UnfinalizedStateStorage uss = unfinalizedStateStorage; + AbstractAsset generalAssetInterface = AbstractAsset(args.assetContract); + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + LinearSum ls = linearSum; + + bytes32 unfinalizedState = uss.getUnfinalizedState(args.tokenId); + uint256 unfinalizedStateIndex = generalAssetInterface.getAssertionIdsLength(args.tokenId); + + if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) + revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); + + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); + + if (!sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + sasProxy.getAgreementScoreFunctionId(agreementId), + block.timestamp + ); + + if (!reqs[2] && !isUpdateCommitWindowOpen(agreementId, args.epoch, unfinalizedStateIndex)) { + uint256 commitWindowEnd = sasProxy.getUpdateCommitsDeadline( + keccak256(abi.encodePacked(agreementId, unfinalizedStateIndex)) + ); + + revert ServiceAgreementErrorsV1U1.CommitWindowClosed( + agreementId, + args.epoch, + unfinalizedStateIndex, + commitWindowEnd - parametersStorage.updateCommitWindowDuration(), + commitWindowEnd, + block.timestamp + ); + } + + uint72 identityId = identityStorage.getIdentityId(msg.sender); + + if (!reqs[3] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( + agreementId, + args.epoch, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + uint40 score = ls.calculateScore( + ls.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), + maxDistance, + nodesCount, + stakingStorage.totalStakes(identityId) + ); + + _insertCommit(agreementId, args.epoch, unfinalizedStateIndex, identityId, 0, 0, score); + + emit CommitSubmitted( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + unfinalizedStateIndex, + identityId, + score + ); + + if ( + sasProxy.getCommitsCount(keccak256(abi.encodePacked(agreementId, args.epoch, unfinalizedStateIndex))) == + parametersStorage.finalizationCommitsNumber() + ) { + if (sasProxy.agreementV1Exists(agreementId)) sasProxy.migrateV1ServiceAgreement(agreementId); + + sasProxy.setAgreementTokenAmount( + agreementId, + sasProxy.getAgreementTokenAmount(agreementId) + sasProxy.getAgreementUpdateTokenAmount(agreementId) + ); + sasProxy.setAgreementUpdateTokenAmount(agreementId, 0); + + ContentAssetStorageV2 cas = contentAssetStorage; + cas.setAssertionIssuer(args.tokenId, unfinalizedState, uss.getIssuer(args.tokenId)); + cas.pushAssertionId(args.tokenId, unfinalizedState); + + uss.deleteIssuer(args.tokenId); + uss.deleteUnfinalizedState(args.tokenId); + + emit StateFinalized( + args.assetContract, + args.tokenId, + args.keyword, + args.hashFunctionId, + args.epoch, + unfinalizedStateIndex, + unfinalizedState + ); + } + } + + function setReq(uint256 index, bool req) external onlyHubOwner { + reqs[index] = req; + } + + function _verifyNeighborhood( + bytes32 agreementId, + uint16 epoch, + uint8 hashFunctionId, + bytes calldata keyword, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex + ) internal virtual returns (uint72, uint256) { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + LinearSum ls = linearSum; + + ( + ShardingTableStructsV2.Node memory leftEdgeNode, + ShardingTableStructsV2.Node memory closestNode, + ShardingTableStructsV2.Node memory rightEdgeNode + ) = sts.getNeighborhoodBoundaryByIndexes(leftEdgeNodeIndex, closestNodeIndex, rightEdgeNodeIndex); + + bool isBetween = (leftEdgeNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + ? (closestNode.hashRingPosition >= leftEdgeNode.hashRingPosition && + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + : (leftEdgeNode.hashRingPosition <= closestNode.hashRingPosition || + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); + + uint72 nodesCount = sts.nodesCount(); + uint72 neighborhoodSize = (leftEdgeNode.index <= rightEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index + 1 + : (nodesCount - leftEdgeNode.index) + rightEdgeNode.index + 1; + + (uint72 closestPrevIdentityId, uint72 closestNextIdentityId) = sts.getAdjacentIdentityIdsByIndex( + closestNodeIndex + ); + uint72 rightEdgeNextIdentityId = rightEdgeNodeIndex != nodesCount - 1 + ? sts.indexToIdentityId(rightEdgeNodeIndex + 1) + : sts.indexToIdentityId(0); + uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 + ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) + : sts.indexToIdentityId(nodesCount - 1); + + (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = linearSum + .calculateNeighborhoodBoundaryDistances( + hashFunctionId, + leftEdgeNode.nodeId, + closestNode.nodeId, + rightEdgeNode.nodeId, + keyword + ); + + // Verify that closestNode is in smaller arc between leftNode and rightNode + if (!isBetween) + revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( + agreementId, + epoch, + closestNodeIndex, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + block.timestamp + ); + + // Verify number of nodes between leftNode and rightNode (should be R2) + if (neighborhoodSize != parametersStorage.r2()) + revert CommitManagerErrorsV2.InvalidNeighborhoodSize( + agreementId, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + nodesCount, + parametersStorage.r2(), + neighborhoodSize, + block.timestamp + ); + + // Verify that closestNode is indeed closest + if ( + closestDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(closestPrevIdentityId), keyword) || + closestDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(closestNextIdentityId), keyword) + ) + revert CommitManagerErrorsV2.InvalidClosestNode( + agreementId, + epoch, + closestNodeIndex, + closestDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(closestPrevIdentityId), keyword), + ls.calculateDistance(hashFunctionId, ps.getNodeId(closestNextIdentityId), keyword), + block.timestamp + ); + + // Verify that leftNode is indeed the left edge of the Neighborhood + if (leftEdgeDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(rightEdgeNextIdentityId), keyword)) + revert CommitManagerErrorsV2.InvalidLeftEdgeNode( + agreementId, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex != nodesCount - 1 ? rightEdgeNodeIndex + 1 : 0, + leftEdgeDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(rightEdgeNextIdentityId), keyword), + block.timestamp + ); + + // Verify that rightNode is indeed the right edge of the Neighborhood + if (rightEdgeDistance > ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword)) + revert CommitManagerErrorsV2.InvalidRightEdgeNode( + agreementId, + epoch, + rightEdgeNodeIndex, + leftEdgeNodeIndex != 0 ? leftEdgeNodeIndex - 1 : nodesCount - 1, + rightEdgeDistance, + ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword), + block.timestamp + ); + + return (nodesCount, (leftEdgeDistance > rightEdgeDistance) ? leftEdgeDistance : rightEdgeDistance); + } + + function _insertCommit( + bytes32 agreementId, + uint16 epoch, + uint256 stateIndex, + uint72 identityId, + uint72 prevIdentityId, + uint72 nextIdentityId, + uint40 score + ) internal virtual { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 commitId = keccak256(abi.encodePacked(agreementId, epoch, stateIndex, identityId)); + + if (!reqs[4] && sasProxy.commitSubmissionExists(commitId)) + revert ServiceAgreementErrorsV1U1.NodeAlreadySubmittedCommit( + agreementId, + epoch, + stateIndex, + identityId, + profileStorage.getNodeId(identityId) + ); + + bytes32 refCommitId = sasProxy.getV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex); + + ParametersStorage params = parametersStorage; + + uint72 refCommitNextIdentityId = sasProxy.getCommitSubmissionNextIdentityId(refCommitId); + uint32 r0 = params.r0(); + uint8 i; + while ((score < sasProxy.getCommitSubmissionScore(refCommitId)) && (refCommitNextIdentityId != 0) && (i < r0)) { + refCommitId = keccak256(abi.encodePacked(agreementId, epoch, stateIndex, refCommitNextIdentityId)); + + refCommitNextIdentityId = sasProxy.getCommitSubmissionNextIdentityId(refCommitId); + unchecked { + i++; + } + } + + if (!reqs[5] && (i >= r0)) + revert ServiceAgreementErrorsV1U1.NodeNotAwarded( + agreementId, + epoch, + stateIndex, + identityId, + profileStorage.getNodeId(identityId), + i + ); + + sasProxy.createV1U1CommitSubmissionObject(commitId, identityId, prevIdentityId, nextIdentityId, score); + + ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); + + if ((i == 0) && (refCommit.identityId == 0)) + // No head -> Setting new head + sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex, commitId); + else if ((i == 0) && (score <= refCommit.score)) + // There is a head with higher or equal score, add new commit on the right + _linkCommits(agreementId, epoch, stateIndex, refCommit.identityId, identityId); + else if ((i == 0) && (score > refCommit.score)) { + // There is a head with lower score, replace the head + sasProxy.setV1U1AgreementEpochSubmissionHead(agreementId, epoch, stateIndex, commitId); + _linkCommits(agreementId, epoch, stateIndex, identityId, refCommit.identityId); + } else if (score > refCommit.score) { + // [H] - head + // [RC] - reference commit + // [RC-] - commit before reference commit + // [RC+] - commit after reference commit + // [NC] - new commit + // [] <-> [H] <-> [X] ... [RC-] <-> [RC] <-> [RC+] ... [C] <-> [] + // [] <-> [H] <-> [X] ... [RC-] <-(NL)-> [NC] <-(NL)-> [RC] <-> [RC+] ... [C] <-> [] + _linkCommits(agreementId, epoch, stateIndex, refCommit.prevIdentityId, identityId); + _linkCommits(agreementId, epoch, stateIndex, identityId, refCommit.identityId); + } + // [] <-> [H] <-> [RC] <-> [] + // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] + else _linkCommits(agreementId, epoch, stateIndex, refCommit.identityId, identityId); + + sasProxy.incrementCommitsCount(keccak256(abi.encodePacked(agreementId, epoch, stateIndex))); + } + + function _linkCommits( + bytes32 agreementId, + uint16 epoch, + uint256 stateIndex, + uint72 leftIdentityId, + uint72 rightIdentityId + ) internal virtual { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + sasProxy.setCommitSubmissionNextIdentityId( + keccak256(abi.encodePacked(agreementId, epoch, stateIndex, leftIdentityId)), // leftCommitId + rightIdentityId + ); + + sasProxy.setCommitSubmissionPrevIdentityId( + keccak256(abi.encodePacked(agreementId, epoch, stateIndex, rightIdentityId)), // rightCommitId + leftIdentityId + ); + } +} diff --git a/contracts/v2/ProximityScoringProxy.sol b/contracts/v2/ProximityScoringProxy.sol new file mode 100644 index 00000000..f0713009 --- /dev/null +++ b/contracts/v2/ProximityScoringProxy.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {ContractStatus} from "../v1/abstract/ContractStatus.sol"; +import {IProximityScoreFunctionsPair} from "./interface/IProximityScoreFunctionsPair.sol"; +import {IScoreFunction} from "../v1/interface/IScoreFunction.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; +import {UnorderedIndexableContractDynamicSetLib} from "../v1/utils/UnorderedIndexableContractDynamicSet.sol"; + +contract ProximityScoringProxy is Named, Versioned, ContractStatus { + using UnorderedIndexableContractDynamicSetLib for UnorderedIndexableContractDynamicSetLib.Set; + + event NewScoringFunctionContract(uint8 indexed scoreFunctionId, address newContractAddress); + event ScoringFunctionContractUpdated(uint8 indexed scoreFunctionId, address newContractAddress); + + string private constant _NAME = "ScoringProxy"; + string private constant _VERSION = "2.0.0"; + + UnorderedIndexableContractDynamicSetLib.Set internal scoreFunctionSet; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) ContractStatus(hubAddress) {} + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function setContractAddress(uint8 scoreFunctionId, address scoringContractAddress) external onlyHubOwner { + if (scoreFunctionSet.exists(scoreFunctionId)) { + emit ScoringFunctionContractUpdated(scoreFunctionId, scoringContractAddress); + scoreFunctionSet.update(scoreFunctionId, scoringContractAddress); + } else { + emit NewScoringFunctionContract(scoreFunctionId, scoringContractAddress); + scoreFunctionSet.append(scoreFunctionId, scoringContractAddress); + } + } + + function removeContract(uint8 scoreFunctionId) external onlyHubOwner { + scoreFunctionSet.remove(scoreFunctionId); + } + + function callScoreFunction( + uint8 scoreFunctionId, + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword, + uint96 stake + ) external view returns (uint40) { + IScoreFunction scoringFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); + uint256 distance = scoringFunction.calculateDistance(hashFunctionId, nodeId, keyword); + return scoringFunction.calculateScore(distance, stake); + } + + function callScoreFunction( + uint8 scoreFunctionId, + uint256 distance, + uint256 maxDistance, + uint72 nodesCount, + uint96 stake + ) external view returns (uint40) { + IProximityScoreFunctionsPair proximityScoreFunctionsPair = IProximityScoreFunctionsPair( + scoreFunctionSet.get(scoreFunctionId).addr + ); + + return proximityScoreFunctionsPair.calculateScore(distance, maxDistance, nodesCount, stake); + } + + function callProximityFunction( + uint8 proximityFunctionId, + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) external view returns (uint256) { + IProximityScoreFunctionsPair proximityScoreFunctionsPair = IProximityScoreFunctionsPair( + scoreFunctionSet.get(proximityFunctionId).addr + ); + + return proximityScoreFunctionsPair.calculateDistance(hashFunctionId, nodeId, keyword); + } + + function getScoreFunctionName(uint8 scoreFunctionId) external view returns (string memory) { + return Named(scoreFunctionSet.get(scoreFunctionId).addr).name(); + } + + function getScoreFunctionContractAddress(uint8 scoreFunctionId) external view returns (address) { + return scoreFunctionSet.get(scoreFunctionId).addr; + } + + function getAllScoreFunctions() external view returns (UnorderedIndexableContractDynamicSetLib.Contract[] memory) { + return scoreFunctionSet.getAll(); + } + + function isScoreFunction(uint8 scoreFunctionId) external view returns (bool) { + return scoreFunctionSet.exists(scoreFunctionId); + } +} diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol new file mode 100644 index 00000000..c651ce07 --- /dev/null +++ b/contracts/v2/ShardingTable.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; +import {ShardingTableStorageV2} from "./storage/ShardingTableStorage.sol"; +import {StakingStorage} from "../v1/storage/StakingStorage.sol"; +import {ContractStatus} from "../v1/abstract/ContractStatus.sol"; +import {Initializable} from "../v1/interface/Initializable.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; +import {ShardingTableStructsV1} from "../v1/structs/ShardingTableStructsV1.sol"; +import {ShardingTableStructsV2} from "./structs/ShardingTableStructsV2.sol"; +import {ShardingTableErrors} from "./errors/ShardingTableErrors.sol"; + +import {NULL} from "../v1/constants/ShardingTableConstants.sol"; + +contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { + event NodeAdded(uint72 indexed identityId, bytes nodeId, uint96 ask, uint96 stake); + event NodeRemoved(uint72 indexed identityId, bytes nodeId); + + string private constant _NAME = "ShardingTable"; + string private constant _VERSION = "2.0.0"; + + ProfileStorage public profileStorage; + ShardingTableStorageV2 public shardingTableStorage; + StakingStorage public stakingStorage; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) ContractStatus(hubAddress) {} + + function initialize() public onlyHubOwner { + profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); + shardingTableStorage = ShardingTableStorageV2(hub.getContractAddress("ShardingTableStorage")); + stakingStorage = StakingStorage(hub.getContractAddress("StakingStorage")); + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function getShardingTable( + uint72 startingIdentityId, + uint72 nodesNumber + ) external view returns (ShardingTableStructsV1.NodeInfo[] memory) { + return _getShardingTable(startingIdentityId, nodesNumber); + } + + function getShardingTable() external view returns (ShardingTableStructsV1.NodeInfo[] memory) { + ShardingTableStorageV2 sts = shardingTableStorage; + return _getShardingTable(sts.indexToIdentityId(0), sts.nodesCount()); + } + + function insertNode(uint72 identityId) external onlyContracts { + uint256 newNodeHashRingPosition = uint256(profileStorage.getNodeAddress(identityId, 1)); + + _insertNode(_binarySearchForIndex(newNodeHashRingPosition), identityId, newNodeHashRingPosition); + } + + function insertNode(uint72 index, uint72 identityId) external onlyContracts { + _insertNode(index, identityId, uint256(profileStorage.getNodeAddress(identityId, 1))); + } + + function removeNode(uint72 identityId) external onlyContracts { + ShardingTableStorageV2 sts = shardingTableStorage; + + ShardingTableStructsV2.Node memory nodeToRemove = sts.getNode(identityId); + + // Decrement indexes of all nodes after removed one + add pointers to identityId for changed indexes + uint72 index = nodeToRemove.index; + uint72 nextIdentityId = sts.indexToIdentityId(nodeToRemove.index + 1); + while (nextIdentityId != NULL) { + sts.decrementNodeIndex(nextIdentityId); + sts.setIdentityId(index, nextIdentityId); + + unchecked { + index += 1; + } + + nextIdentityId = sts.indexToIdentityId(index + 1); + } + + // Delete node object + set last index pointer to be NULL + decrement total nodes count + sts.deleteNodeObject(identityId); + sts.setIdentityId(index, NULL); + sts.decrementNodesCount(); + + emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); + } + + function _binarySearchForIndex(uint256 hashRingPosition) internal virtual returns (uint72) { + ShardingTableStorageV2 sts = shardingTableStorage; + + uint72 nodesCount = sts.nodesCount(); + + if (nodesCount == 0) { + return 0; + } + + uint72 left; + uint72 mid; + uint72 right = nodesCount - 1; + + while (left <= right) { + mid = (left + right) / 2; + uint256 currentHashRingPosition = sts.getHashRingPositionByIndex(mid); + + if (hashRingPosition > currentHashRingPosition) { + // Node is in the right half of the range, move left pointers + left = mid + 1; + } else if (hashRingPosition < currentHashRingPosition) { + if (mid == 0) { + // The new element should be inserted at index 0 + return 0; + } + // Node is in the left half of the range, move right pointers + right = mid - 1; + } else { + // Exact match found + return mid; + } + } + + return left; + } + + function _insertNode(uint72 index, uint72 identityId, uint256 newNodeHashRingPosition) internal virtual { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + + if (index != 0) { + uint256 prevNodeHashRingPosition = sts.getHashRingPositionByIndex(index - 1); + + // Check that the new Node is indeed on the right from the prevNode + // Also allows new Head insertion as prevNode.hashRingPosition will be 0 in such case + if (newNodeHashRingPosition < prevNodeHashRingPosition) + revert ShardingTableErrors.InvalidIndexWithRespectToPreviousNode( + identityId, + newNodeHashRingPosition, + prevNodeHashRingPosition + ); + } + + ShardingTableStructsV2.Node memory nextNode = sts.getNodeByIndex(index); + + // Check that the new Node is indeed on the left from the nextNode + // Check is skipped when inserting new Tail + if (nextNode.identityId != NULL && newNodeHashRingPosition > nextNode.hashRingPosition) + revert ShardingTableErrors.InvalidIndexWithRespectToNextNode( + identityId, + newNodeHashRingPosition, + nextNode.hashRingPosition + ); + + // Create node object + set index pointer to new identityId + increment total nodes count + sts.createNodeObject(newNodeHashRingPosition, ps.getNodeId(identityId), identityId, index); + sts.setIdentityId(index, identityId); + sts.incrementNodesCount(); + + // Increment indexes of all nodes after inserted one + add pointers to identityId for changed indexes + uint72 currentIdentityId; + uint72 nextIdentityId = nextNode.identityId; + while (nextIdentityId != NULL) { + unchecked { + index += 1; + } + + currentIdentityId = nextIdentityId; + nextIdentityId = sts.indexToIdentityId(index); + + sts.incrementNodeIndex(currentIdentityId); + sts.setIdentityId(index, currentIdentityId); + } + + emit NodeAdded( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + function _getShardingTable( + uint72 startingIdentityId, + uint72 nodesNumber + ) internal view virtual returns (ShardingTableStructsV1.NodeInfo[] memory) { + ShardingTableStructsV1.NodeInfo[] memory nodesPage; + ShardingTableStorageV2 sts = shardingTableStorage; + + if ((sts.nodesCount() == 0) || (nodesNumber == 0)) return nodesPage; + + ShardingTableStructsV2.Node memory startingNode = sts.getNode(startingIdentityId); + + require((startingIdentityId == NULL) || (startingNode.identityId != NULL), "Wrong starting Identity ID"); + + nodesPage = new ShardingTableStructsV1.NodeInfo[](nodesNumber); + + ProfileStorage ps = profileStorage; + StakingStorage ss = stakingStorage; + + uint72 nextIdentityId = startingIdentityId; + uint72 index; + while ((index < nodesNumber) && (nextIdentityId != NULL)) { + nodesPage[index] = ShardingTableStructsV1.NodeInfo({ + nodeId: ps.getNodeId(nextIdentityId), + identityId: nextIdentityId, + ask: ps.getAsk(nextIdentityId), + stake: ss.totalStakes(nextIdentityId) + }); + + unchecked { + index += 1; + } + nextIdentityId = sts.indexToIdentityId(index + startingNode.index); + } + + return nodesPage; + } +} diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol new file mode 100644 index 00000000..c7d1a3c0 --- /dev/null +++ b/contracts/v2/Staking.sol @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {ShardingTableV2} from "./ShardingTable.sol"; +import {Shares} from "../v1/Shares.sol"; +import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; +import {NodeOperatorFeeChangesStorage} from "./storage/NodeOperatorFeeChangesStorage.sol"; +import {ParametersStorage} from "../v1/storage/ParametersStorage.sol"; +import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; +import {ServiceAgreementStorageProxy} from "../v1/storage/ServiceAgreementStorageProxy.sol"; +import {ShardingTableStorageV2} from "./storage/ShardingTableStorage.sol"; +import {StakingStorage} from "../v1/storage/StakingStorage.sol"; +import {ContractStatus} from "../v1/abstract/ContractStatus.sol"; +import {Initializable} from "../v1/interface/Initializable.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; +import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; +import {ProfileErrors} from "./errors/ProfileErrors.sol"; +import {StakingErrors} from "./errors/StakingErrors.sol"; +import {TokenErrors} from "../v1/errors/TokenErrors.sol"; +import {ADMIN_KEY} from "../v1/constants/IdentityConstants.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract StakingV2 is Named, Versioned, ContractStatus, Initializable { + event StakeIncreased( + uint72 indexed identityId, + bytes nodeId, + address indexed staker, + uint96 oldStake, + uint96 newStake + ); + event SharesMinted( + address indexed sharesContractAddress, + address indexed delegator, + uint256 sharesMintedAmount, + uint256 newTotalSupply + ); + event RewardCollected( + uint72 indexed identityId, + bytes nodeId, + address serviceAgreementAddress, + uint96 nodeOperatorFee, + uint96 delegatorsReward + ); + event StakeWithdrawalStarted( + uint72 indexed identityId, + bytes nodeId, + address indexed staker, + uint96 oldStake, + uint96 newStake, + uint256 withdrawalPeriodEnd + ); + event StakeWithdrawn(uint72 indexed identityId, bytes nodeId, address indexed staker, uint96 withdrawnStakeAmount); + event SharesBurned( + address indexed sharesContractAddress, + address indexed delegator, + uint256 sharesBurnedAmount, + uint256 newTotalSupply + ); + event AccumulatedOperatorFeeIncreased( + uint72 indexed identityId, + bytes nodeId, + uint96 oldAccumulatedOperatorFee, + uint96 newAccumulatedOperatorFee + ); + event OperatorFeeChangeStarted(uint72 indexed identityId, bytes nodeId, uint8 operatorFee, uint256 timestamp); + event OperatorFeeChangeFinished(uint72 indexed identityId, bytes nodeId, uint8 operatorFee); + + string private constant _NAME = "Staking"; + string private constant _VERSION = "2.0.0"; + + ShardingTableV2 public shardingTableContract; + IdentityStorageV2 public identityStorage; + NodeOperatorFeeChangesStorage public nodeOperatorFeeChangesStorage; + ParametersStorage public parametersStorage; + ProfileStorage public profileStorage; + StakingStorage public stakingStorage; + ServiceAgreementStorageProxy public serviceAgreementStorageProxy; + ShardingTableStorageV2 public shardingTableStorage; + IERC20 public tokenContract; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) ContractStatus(hubAddress) {} + + modifier onlyAdmin(uint72 identityId) { + _checkAdmin(identityId); + _; + } + + function initialize() public onlyHubOwner { + shardingTableContract = ShardingTableV2(hub.getContractAddress("ShardingTable")); + identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); + nodeOperatorFeeChangesStorage = NodeOperatorFeeChangesStorage( + hub.getContractAddress("NodeOperatorFeeChangesStorage") + ); + parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); + profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); + stakingStorage = StakingStorage(hub.getContractAddress("StakingStorage")); + serviceAgreementStorageProxy = ServiceAgreementStorageProxy( + hub.getContractAddress("ServiceAgreementStorageProxy") + ); + shardingTableStorage = ShardingTableStorageV2(hub.getContractAddress("ShardingTableStorage")); + tokenContract = IERC20(hub.getContractAddress("Token")); + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function addStake(address sender, uint72 identityId, uint96 stakeAmount) external onlyContracts { + _addStake(sender, identityId, stakeAmount); + } + + function addStake(uint72 identityId, uint96 stakeAmount) external { + _addStake(msg.sender, identityId, stakeAmount); + } + + function startStakeWithdrawal(uint72 identityId, uint96 sharesToBurn) external { + if (sharesToBurn == 0) revert StakingErrors.ZeroSharesAmount(); + + ProfileStorage ps = profileStorage; + StakingStorage ss = stakingStorage; + + if (!ps.profileExists(identityId)) revert ProfileErrors.ProfileDoesntExist(identityId); + + Shares sharesContract = Shares(ps.getSharesContractAddress(identityId)); + + if (sharesToBurn > sharesContract.balanceOf(msg.sender)) + revert TokenErrors.TooLowBalance(address(sharesContract), sharesContract.balanceOf(msg.sender)); + + uint96 oldStake = ss.totalStakes(identityId); + uint96 stakeWithdrawalAmount = uint96((uint256(oldStake) * sharesToBurn) / sharesContract.totalSupply()); + uint96 newStake = oldStake - stakeWithdrawalAmount; + uint96 newStakeWithdrawalAmount = ss.getWithdrawalRequestAmount(identityId, msg.sender) + stakeWithdrawalAmount; + + ParametersStorage params = parametersStorage; + + uint256 withdrawalPeriodEnd = block.timestamp + params.stakeWithdrawalDelay(); + ss.createWithdrawalRequest(identityId, msg.sender, newStakeWithdrawalAmount, withdrawalPeriodEnd); + ss.setTotalStake(identityId, newStake); + sharesContract.burnFrom(msg.sender, sharesToBurn); + + if (shardingTableStorage.nodeExists(identityId) && (newStake < params.minimumStake())) + shardingTableContract.removeNode(identityId); + + emit StakeWithdrawalStarted( + identityId, + ps.getNodeId(identityId), + msg.sender, + oldStake, + newStake, + withdrawalPeriodEnd + ); + emit SharesBurned(address(sharesContract), msg.sender, sharesToBurn, sharesContract.totalSupply()); + } + + function withdrawStake(uint72 identityId) external { + ProfileStorage ps = profileStorage; + + if (!ps.profileExists(identityId)) revert ProfileErrors.ProfileDoesntExist(identityId); + + StakingStorage ss = stakingStorage; + + uint96 stakeWithdrawalAmount; + uint256 withdrawalTimestamp; + (stakeWithdrawalAmount, withdrawalTimestamp) = ss.withdrawalRequests(identityId, msg.sender); + + if (stakeWithdrawalAmount == 0) revert StakingErrors.WithdrawalWasntInitiated(); + if (block.timestamp < withdrawalTimestamp) + revert StakingErrors.WithdrawalPeriodPending(block.timestamp, withdrawalTimestamp); + + ss.deleteWithdrawalRequest(identityId, msg.sender); + ss.transferStake(msg.sender, stakeWithdrawalAmount); + + emit StakeWithdrawn(identityId, ps.getNodeId(identityId), msg.sender, stakeWithdrawalAmount); + } + + function addReward(bytes32 agreementId, uint72 identityId, uint96 rewardAmount) external onlyContracts { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + StakingStorage ss = stakingStorage; + + uint96 operatorFee = (rewardAmount * ss.operatorFees(identityId)) / 100; + uint96 delegatorsReward = rewardAmount - operatorFee; + + ProfileStorage ps = profileStorage; + + uint96 oldAccumulatedOperatorFee = ps.getAccumulatedOperatorFee(identityId); + uint96 oldStake = ss.totalStakes(identityId); + + if (operatorFee != 0) { + ps.setAccumulatedOperatorFee(identityId, oldAccumulatedOperatorFee + operatorFee); + sasProxy.transferAgreementTokens(agreementId, address(ps), operatorFee); + } + + if (delegatorsReward != 0) { + ss.setTotalStake(identityId, oldStake + delegatorsReward); + sasProxy.transferAgreementTokens(agreementId, address(ss), delegatorsReward); + + if (!shardingTableStorage.nodeExists(identityId) && oldStake >= parametersStorage.minimumStake()) + shardingTableContract.insertNode(identityId); + } + + emit AccumulatedOperatorFeeIncreased( + identityId, + ps.getNodeId(identityId), + oldAccumulatedOperatorFee, + oldAccumulatedOperatorFee + operatorFee + ); + + address sasAddress; + if (sasProxy.agreementV1Exists(agreementId)) sasAddress = sasProxy.agreementV1StorageAddress(); + else sasAddress = sasProxy.agreementV1U1StorageAddress(); + + emit StakeIncreased(identityId, ps.getNodeId(identityId), sasAddress, oldStake, oldStake + delegatorsReward); + emit RewardCollected(identityId, ps.getNodeId(identityId), sasAddress, operatorFee, delegatorsReward); + } + + // solhint-disable-next-line no-empty-blocks + function slash(uint72 identityId) external onlyContracts { + // TBD + } + + function startOperatorFeeChange(uint72 identityId, uint8 newOperatorFee) external onlyAdmin(identityId) { + if (newOperatorFee > 100) revert StakingErrors.InvalidOperatorFee(); + + NodeOperatorFeeChangesStorage nofcs = nodeOperatorFeeChangesStorage; + + uint256 feeChangeDelayEnd = block.timestamp > nofcs.delayFreePeriodEnd() + ? block.timestamp + parametersStorage.stakeWithdrawalDelay() + : block.timestamp; + nofcs.createOperatorFeeChangeRequest(identityId, newOperatorFee, feeChangeDelayEnd); + + emit OperatorFeeChangeStarted( + identityId, + profileStorage.getNodeId(identityId), + newOperatorFee, + feeChangeDelayEnd + ); + } + + function finishOperatorFeeChange(uint72 identityId) external onlyAdmin(identityId) { + NodeOperatorFeeChangesStorage nofcs = nodeOperatorFeeChangesStorage; + + uint8 newFee; + uint256 feeChangeDelayEnd; + (newFee, feeChangeDelayEnd) = nofcs.operatorFeeChangeRequests(identityId); + + if (block.timestamp < feeChangeDelayEnd) + revert StakingErrors.OperatorFeeChangeDelayPending(block.timestamp, feeChangeDelayEnd); + + stakingStorage.setOperatorFee(identityId, newFee); + nofcs.deleteOperatorFeeChangeRequest(identityId); + + emit OperatorFeeChangeFinished(identityId, profileStorage.getNodeId(identityId), newFee); + } + + function _addStake(address sender, uint72 identityId, uint96 stakeAmount) internal virtual { + StakingStorage ss = stakingStorage; + ProfileStorage ps = profileStorage; + ParametersStorage params = parametersStorage; + IERC20 tknc = tokenContract; + + uint96 oldStake = ss.totalStakes(identityId); + uint96 newStake = oldStake + stakeAmount; + + if (!ps.profileExists(identityId)) revert ProfileErrors.ProfileDoesntExist(identityId); + if (stakeAmount > tknc.allowance(sender, address(this))) + revert TokenErrors.TooLowAllowance(address(tknc), tknc.allowance(sender, address(this))); + if (newStake > params.maximumStake()) revert StakingErrors.MaximumStakeExceeded(params.maximumStake()); + + Shares sharesContract = Shares(ps.getSharesContractAddress(identityId)); + + uint256 sharesMinted; + if (sharesContract.totalSupply() == 0) sharesMinted = stakeAmount; + else sharesMinted = ((uint256(stakeAmount) * sharesContract.totalSupply()) / oldStake); + + sharesContract.mint(sender, sharesMinted); + + ss.setTotalStake(identityId, newStake); + tknc.transferFrom(sender, address(ss), stakeAmount); + + if (!shardingTableStorage.nodeExists(identityId) && newStake >= params.minimumStake()) + shardingTableContract.insertNode(identityId); + + emit StakeIncreased(identityId, ps.getNodeId(identityId), sender, oldStake, newStake); + emit SharesMinted(address(sharesContract), sender, sharesMinted, sharesContract.totalSupply()); + } + + function _checkAdmin(uint72 identityId) internal view virtual { + if (!identityStorage.keyHasPurpose(identityId, keccak256(abi.encodePacked(msg.sender)), ADMIN_KEY)) + revert GeneralErrors.OnlyProfileAdminFunction(msg.sender); + } +} diff --git a/contracts/v2/errors/CommitManagerErrorsV2.sol b/contracts/v2/errors/CommitManagerErrorsV2.sol new file mode 100644 index 00000000..e11d0a73 --- /dev/null +++ b/contracts/v2/errors/CommitManagerErrorsV2.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library CommitManagerErrorsV2 { + error ClosestNodeNotInNeighborhood( + bytes32 agreementId, + uint16 epoch, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex, + uint256 timeNow + ); + error InvalidNeighborhoodSize( + bytes32 agreementId, + uint16 epoch, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex, + uint72 numberOfNodes, + uint256 neighborhoodExpectedSize, + uint256 neighborhoodActualSize, + uint256 timeNow + ); + error InvalidClosestNode( + bytes32 agreementId, + uint16 epoch, + uint72 closestNodeIndex, + uint256 closestNodeDistance, + uint256 leftAdjacentDistance, + uint256 rightAdjacentDistance, + uint256 timeNow + ); + error InvalidLeftEdgeNode( + bytes32 agreementId, + uint16 epoch, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeAdjacentIndex, + uint256 leftEdgeNodeDistance, + uint256 rightEdgeNodeAdjacentDistance, + uint256 timeNow + ); + error InvalidRightEdgeNode( + bytes32 agreementId, + uint16 epoch, + uint72 rightEdgeNodeIndex, + uint72 leftEdgeNodeAdjacentIndex, + uint256 rightEdgeNodeDistance, + uint256 leftEdgeNodeAdjacentDistance, + uint256 timeNow + ); +} diff --git a/contracts/v2/errors/ProfileErrors.sol b/contracts/v2/errors/ProfileErrors.sol new file mode 100644 index 00000000..117c9dc5 --- /dev/null +++ b/contracts/v2/errors/ProfileErrors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ProfileErrors { + error ProfileDoesntExist(uint72 identityId); +} diff --git a/contracts/v2/errors/ServiceAgreementErrorsV2.sol b/contracts/v2/errors/ServiceAgreementErrorsV2.sol new file mode 100644 index 00000000..690a65ac --- /dev/null +++ b/contracts/v2/errors/ServiceAgreementErrorsV2.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ServiceAgreementErrorsV2 { + error InvalidProximityScoreFunctionsPairId( + bytes32 agreementId, + uint16 epoch, + uint8 agreementScoreFunctionId, + uint256 timeNow + ); +} diff --git a/contracts/v2/errors/ShardingTableErrors.sol b/contracts/v2/errors/ShardingTableErrors.sol new file mode 100644 index 00000000..f17212f5 --- /dev/null +++ b/contracts/v2/errors/ShardingTableErrors.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ShardingTableErrors { + error InvalidIndexWithRespectToPreviousNode( + uint72 identityId, + uint256 hashRingPosition, + uint256 prevHashRingPosition + ); + error InvalidIndexWithRespectToNextNode(uint72 identityId, uint256 hashRingPosition, uint256 nextHashRingPosition); +} diff --git a/contracts/v2/errors/StakingErrors.sol b/contracts/v2/errors/StakingErrors.sol new file mode 100644 index 00000000..ebd3b984 --- /dev/null +++ b/contracts/v2/errors/StakingErrors.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library StakingErrors { + error ZeroSharesAmount(); + error WithdrawalWasntInitiated(); + error WithdrawalPeriodPending(uint256 nowTimestamp, uint256 endTimestamp); + error InvalidOperatorFee(); + error OperatorFeeChangeDelayPending(uint256 nowTimestamp, uint256 endTimestamp); + error MaximumStakeExceeded(uint256 amount); +} diff --git a/contracts/v2/interface/IProximityScoreFunctionsPair.sol b/contracts/v2/interface/IProximityScoreFunctionsPair.sol new file mode 100644 index 00000000..0410b290 --- /dev/null +++ b/contracts/v2/interface/IProximityScoreFunctionsPair.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +interface IProximityScoreFunctionsPair { + function calculateScore( + uint256 distance, + uint256 maxDistance, + uint72 maxNodesNumber, + uint96 stake + ) external view returns (uint40); + + function calculateDistance( + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) external view returns (uint256); +} diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol new file mode 100644 index 00000000..ad5736a7 --- /dev/null +++ b/contracts/v2/scoring/LinearSum.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HashingProxy} from "../../v1/HashingProxy.sol"; +import {ParametersStorage} from "../../v1/storage/ParametersStorage.sol"; +import {HubDependent} from "../../v1/abstract/HubDependent.sol"; +import {Indexable} from "../../v1/interface/Indexable.sol"; +import {Initializable} from "../../v1/interface/Initializable.sol"; +import {IProximityScoreFunctionsPair} from "../interface/IProximityScoreFunctionsPair.sol"; +import {Named} from "../../v1/interface/Named.sol"; +import {ScaleDownLib} from "../utils/ScaleDownLibrary.sol"; + +contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDependent, Initializable { + event ParameterChanged(string parameterName, uint256 parameterValue); + + uint8 private constant _ID = 2; + string private constant _NAME = "LinearSum"; + + uint256 constant HASH_RING_SIZE = type(uint256).max; + + HashingProxy public hashingProxy; + ParametersStorage public parametersStorage; + + uint96 public distanceScaleFactor; + uint96 public stakeScaleFactor; + uint32 public w1; + uint32 public w2; + + constructor(address hubAddress) HubDependent(hubAddress) { + distanceScaleFactor = 1e18; + stakeScaleFactor = 1e18; + w1 = 1; + w2 = 1; + } + + function initialize() public onlyHubOwner { + hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); + parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); + } + + function id() external pure virtual override returns (uint8) { + return _ID; + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function calculateScore( + uint256 distance, + uint256 maxDistance, + uint72 maxNodesNumber, + uint96 stake + ) external view returns (uint40) { + uint64 normalizedDistance = normalizeDistance(distance, maxDistance, maxNodesNumber); + + if (1e18 >= normalizedDistance) { + return + ScaleDownLib.toUint40((1e18 - normalizedDistance) * w1 + normalizeStake(stake) * w2, (w1 + w2) * 1e18); + } else { + uint64 proximityScore = (normalizedDistance - 1e18) * w1; + uint64 stakeScore = normalizeStake(stake) * w2; + if (stakeScore <= proximityScore) { + return 0; + } + return ScaleDownLib.toUint40(stakeScore - proximityScore, (w1 + w2) * 1e18); + } + } + + function calculateDistance( + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) public view returns (uint256) { + uint256 nodePositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, nodeId)); + uint256 keywordPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyword)); + + uint256 distanceClockwise = ( + (nodePositionOnHashRing > keywordPositionOnHashRing) + ? nodePositionOnHashRing - keywordPositionOnHashRing + : keywordPositionOnHashRing - nodePositionOnHashRing + ); + + return ( + (distanceClockwise < HASH_RING_SIZE - distanceClockwise) + ? distanceClockwise + : HASH_RING_SIZE - distanceClockwise + ); + } + + function calculateNeighborhoodBoundaryDistances( + uint8 hashFunctionId, + bytes calldata leftEdgeNodeId, + bytes calldata closestEdgeNodeId, + bytes calldata rightEdgeNodeId, + bytes calldata keyword + ) external view returns (uint256, uint256, uint256) { + return ( + calculateDistance(hashFunctionId, leftEdgeNodeId, keyword), + calculateDistance(hashFunctionId, closestEdgeNodeId, keyword), + calculateDistance(hashFunctionId, rightEdgeNodeId, keyword) + ); + } + + function normalizeDistance(uint256 distance, uint256 maxDistance, uint72 nodesCount) public view returns (uint64) { + if (distance == 0) return 0; + + uint256 idealMaxDistance = (HASH_RING_SIZE / nodesCount) * ((parametersStorage.r2() + 1) / 2); + uint256 divisor = (maxDistance <= idealMaxDistance) ? maxDistance : idealMaxDistance; + + uint256 maxMultiplier = type(uint256).max / distance; + + uint256 scaledDistanceScaleFactor = distanceScaleFactor; + uint256 compensationFactor = 1; + + if (scaledDistanceScaleFactor > maxMultiplier) { + compensationFactor = scaledDistanceScaleFactor / maxMultiplier; + scaledDistanceScaleFactor = maxMultiplier; + } + + uint256 scaledDistance = distance * scaledDistanceScaleFactor; + uint256 adjustedDivisor = divisor / compensationFactor; + + return uint64(scaledDistance / adjustedDivisor); + } + + function normalizeStake(uint96 stake) public view returns (uint64) { + ParametersStorage ps = parametersStorage; + + uint96 minStake = ps.minimumStake(); + uint96 maxStake = ps.maximumStake(); + uint96 balancedStake = stake <= maxStake ? stake : maxStake; + + return uint64((uint256(stakeScaleFactor) * (balancedStake - minStake)) / (maxStake - minStake)); + } + + function getParameters() external view returns (uint96, uint96, uint32, uint32) { + return (distanceScaleFactor, stakeScaleFactor, w1, w2); + } + + function setDistanceScaleFactor(uint96 distanceScaleFactor_) external onlyHubOwner { + distanceScaleFactor = distanceScaleFactor_; + + emit ParameterChanged("distanceScaleFactor", distanceScaleFactor); + } + + function setStakeScaleFactor(uint96 stakeScaleFactor_) external onlyHubOwner { + stakeScaleFactor = stakeScaleFactor_; + + emit ParameterChanged("stakeScaleFactor", stakeScaleFactor); + } + + function setW1(uint32 w1_) external onlyHubOwner { + w1 = w1_; + + emit ParameterChanged("w1", w1); + } + + function setW2(uint32 w2_) external onlyHubOwner { + w2 = w2_; + + emit ParameterChanged("w2", w2); + } +} diff --git a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol new file mode 100644 index 00000000..4aa83479 --- /dev/null +++ b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HubDependent} from "../../v1/abstract/HubDependent.sol"; +import {Named} from "../../v1/interface/Named.sol"; +import {Versioned} from "../../v1/interface/Versioned.sol"; + +contract NodeOperatorFeeChangesStorage is Named, Versioned, HubDependent { + string private constant _NAME = "NodeOperatorFeeChangesStorage"; + string private constant _VERSION = "2.0.0"; + + struct OperatorFeeChangeRequest { + uint8 newFee; + uint256 timestamp; + } + + bool private _delayFreePeriodSet; + uint256 public delayFreePeriodEnd; + + // identityId => operatorFeeChangeRequest + mapping(uint72 => OperatorFeeChangeRequest) public operatorFeeChangeRequests; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) HubDependent(hubAddress) {} + + modifier onlyOnce() { + require(!_delayFreePeriodSet, "Function has already been executed."); + _; + _delayFreePeriodSet = true; + } + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function createOperatorFeeChangeRequest(uint72 identityId, uint8 newFee, uint256 timestamp) external onlyContracts { + operatorFeeChangeRequests[identityId] = OperatorFeeChangeRequest({newFee: newFee, timestamp: timestamp}); + } + + function deleteOperatorFeeChangeRequest(uint72 identityId) external onlyContracts { + delete operatorFeeChangeRequests[identityId]; + } + + function getOperatorFeeChangeRequestNewFee(uint72 identityId) external view returns (uint8) { + return operatorFeeChangeRequests[identityId].newFee; + } + + function getOperatorFeeChangeRequestTimestamp(uint72 identityId) external view returns (uint256) { + return operatorFeeChangeRequests[identityId].timestamp; + } + + function operatorFeeChangeRequestExists(uint72 identityId) external view returns (bool) { + return operatorFeeChangeRequests[identityId].timestamp != 0; + } + + function setDelayFreePeriodEnd(uint256 timestamp) external onlyHubOwner onlyOnce { + delayFreePeriodEnd = timestamp; + } +} diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol new file mode 100644 index 00000000..ada90cdf --- /dev/null +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HubDependent} from "../../v1/abstract/HubDependent.sol"; +import {Named} from "../../v1/interface/Named.sol"; +import {Versioned} from "../../v1/interface/Versioned.sol"; +import {ShardingTableStructsV2} from "../structs/ShardingTableStructsV2.sol"; +import {NULL} from "../../v1/constants/ShardingTableConstants.sol"; + +contract ShardingTableStorageV2 is Named, Versioned, HubDependent { + string private constant _NAME = "ShardingTableStorage"; + string private constant _VERSION = "2.0.0"; + + uint72 public nodesCount; + + // identityId => Node + mapping(uint72 => ShardingTableStructsV2.Node) internal nodes; + // index => identityId + mapping(uint72 => uint72) public indexToIdentityId; + + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) HubDependent(hubAddress) {} + + function name() external pure virtual override returns (string memory) { + return _NAME; + } + + function version() external pure virtual override returns (string memory) { + return _VERSION; + } + + function incrementNodesCount() external onlyContracts { + nodesCount++; + } + + function decrementNodesCount() external onlyContracts { + nodesCount--; + } + + function createNodeObject( + uint256 hashRingPosition, + bytes calldata nodeId, + uint72 identityId, + uint72 index + ) external onlyContracts { + nodes[identityId] = ShardingTableStructsV2.Node({ + hashRingPosition: hashRingPosition, + index: index, + nodeId: nodeId, + identityId: identityId + }); + } + + function getNode(uint72 identityId) external view returns (ShardingTableStructsV2.Node memory) { + return nodes[identityId]; + } + + function deleteNodeObject(uint72 identityId) external onlyContracts { + delete nodes[identityId]; + } + + function nodeExists(uint72 identityId) external view returns (bool) { + return nodes[identityId].identityId != 0; + } + + function head() external view returns (uint72) { + return indexToIdentityId[0]; + } + + function getHashRingPosition(uint72 identityId) external view returns (uint256) { + return nodes[identityId].hashRingPosition; + } + + function incrementNodeIndex(uint72 identityId) external onlyContracts { + nodes[identityId].index += 1; + } + + function decrementNodeIndex(uint72 identityId) external onlyContracts { + nodes[identityId].index -= 1; + } + + function setIdentityId(uint72 index, uint72 identityId) external onlyContracts { + indexToIdentityId[index] = identityId; + } + + function getNodeByIndex(uint72 index) external view returns (ShardingTableStructsV2.Node memory) { + return nodes[indexToIdentityId[index]]; + } + + function getNeighborhoodBoundaryByIndexes( + uint72 leftEdgeIndex, + uint72 closestNodeIndex, + uint72 rightEdgeIndex + ) + external + view + returns ( + ShardingTableStructsV2.Node memory, + ShardingTableStructsV2.Node memory, + ShardingTableStructsV2.Node memory + ) + { + return ( + nodes[indexToIdentityId[leftEdgeIndex]], + nodes[indexToIdentityId[closestNodeIndex]], + nodes[indexToIdentityId[rightEdgeIndex]] + ); + } + + function getAdjacentIdentityIdsByIndex(uint72 index) external view returns (uint72, uint72) { + if (index == 0) { + return (NULL, nodes[indexToIdentityId[index + 1]].identityId); + } + + return (nodes[indexToIdentityId[index - 1]].identityId, nodes[indexToIdentityId[index + 1]].identityId); + } + + function getHashRingPositionByIndex(uint72 index) external view returns (uint256) { + return nodes[indexToIdentityId[index]].hashRingPosition; + } + + function nodeExistsByIndex(uint72 index) external view returns (bool) { + return nodes[indexToIdentityId[index]].identityId != 0; + } + + function getMultipleNodes( + uint72 firstIdentityId, + uint16 nodesNumber + ) external view returns (ShardingTableStructsV2.Node[] memory) { + ShardingTableStructsV2.Node[] memory nodesPage = new ShardingTableStructsV2.Node[](nodesNumber); + + ShardingTableStructsV2.Node memory currentNode = nodes[firstIdentityId]; + for (uint256 i; i < nodesNumber; ) { + nodesPage[i] = currentNode; + currentNode = nodes[indexToIdentityId[currentNode.index + 1]]; + unchecked { + i++; + } + } + + return nodesPage; + } +} diff --git a/contracts/v2/storage/assets/ContentAssetStorage.sol b/contracts/v2/storage/assets/ContentAssetStorage.sol index d9af213e..d3b191f5 100644 --- a/contracts/v2/storage/assets/ContentAssetStorage.sol +++ b/contracts/v2/storage/assets/ContentAssetStorage.sol @@ -54,9 +54,7 @@ contract ContentAssetStorageV2 is ContentAssetStorage, IERC4906 { function setBaseURI(string memory baseURI) external virtual onlyHubOwner { tokenBaseURI = baseURI; - if (_tokenId > 1) { - emit BatchMetadataUpdate(1, lastTokenId()); - } + if (_tokenId > 1) emit BatchMetadataUpdate(1, lastTokenId()); } function _baseURI() internal view virtual override returns (string memory) { diff --git a/contracts/v2/structs/ServiceAgreementStructsV2.sol b/contracts/v2/structs/ServiceAgreementStructsV2.sol new file mode 100644 index 00000000..67e47c3e --- /dev/null +++ b/contracts/v2/structs/ServiceAgreementStructsV2.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ServiceAgreementStructsV2 { + struct CommitInputArgs { + address assetContract; + uint256 tokenId; + bytes keyword; + uint8 hashFunctionId; + uint16 epoch; + uint72 closestNodeIndex; + uint72 leftEdgeNodeIndex; + uint72 rightEdgeNodeIndex; + } +} diff --git a/contracts/v2/structs/ShardingTableStructsV2.sol b/contracts/v2/structs/ShardingTableStructsV2.sol new file mode 100644 index 00000000..c6012f3f --- /dev/null +++ b/contracts/v2/structs/ShardingTableStructsV2.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ShardingTableStructsV2 { + struct Node { + uint256 hashRingPosition; + bytes nodeId; + uint72 index; + uint72 identityId; + } +} diff --git a/contracts/v2/utils/ScaleDownLibrary.sol b/contracts/v2/utils/ScaleDownLibrary.sol new file mode 100644 index 00000000..c19bc296 --- /dev/null +++ b/contracts/v2/utils/ScaleDownLibrary.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ScaleDownLib { + function toUint40(uint216 value, uint216 maxValue) internal pure returns (uint40) { + return uint40((value * type(uint40).max) / maxValue); + } +} diff --git a/deploy/013_deploy_sharding_table_storage.ts b/deploy/008_deploy_proximity_scoring_proxy.ts similarity index 62% rename from deploy/013_deploy_sharding_table_storage.ts rename to deploy/008_deploy_proximity_scoring_proxy.ts index 489a7155..50e3c474 100644 --- a/deploy/013_deploy_sharding_table_storage.ts +++ b/deploy/008_deploy_proximity_scoring_proxy.ts @@ -3,10 +3,11 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await hre.helpers.deploy({ - newContractName: 'ShardingTableStorage', + newContractName: 'ProximityScoringProxy', + newContractNameInHub: 'ScoringProxy', }); }; export default func; -func.tags = ['ShardingTableStorage', 'v1']; -func.dependencies = ['Hub']; +func.tags = ['ScoringProxy', 'v2']; +func.dependencies = ['HubV2']; diff --git a/deploy/010_deploy_linear_sum.ts b/deploy/010_deploy_linear_sum.ts new file mode 100644 index 00000000..623516da --- /dev/null +++ b/deploy/010_deploy_linear_sum.ts @@ -0,0 +1,37 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const isDeployed = hre.helpers.isDeployed('LinearSum'); + + const LinearSum = await hre.helpers.deploy({ + newContractName: 'LinearSum', + }); + + if (!isDeployed) { + if (hre.network.config.environment === 'development') { + const { deployer } = await hre.getNamedAccounts(); + + const hubControllerAddress = hre.helpers.contractDeployments.contracts['HubController'].evmAddress; + const HubController = await hre.ethers.getContractAt('HubController', hubControllerAddress, deployer); + + const ScoringProxyAbi = hre.helpers.getAbi('ScoringProxy'); + const ScoringProxyInterface = new hre.ethers.utils.Interface(ScoringProxyAbi); + const scoringProxyAddress = hre.helpers.contractDeployments.contracts['ScoringProxy'].evmAddress; + + const setContractTx = await HubController.forwardCall( + scoringProxyAddress, + ScoringProxyInterface.encodeFunctionData('setContractAddress', [2, LinearSum.address]), + ); + await setContractTx.wait(); + } else { + hre.helpers.newScoreFunctions.push(LinearSum.address); + } + } + + await hre.helpers.updateContractParameters('LinearSum', LinearSum); +}; + +export default func; +func.tags = ['LinearSum', 'v2']; +func.dependencies = ['HubV2', 'HashingProxy', 'SHA256', 'ScoringProxy', 'ParametersStorage']; diff --git a/deploy/010_deploy_assertion_storage.ts b/deploy/011_deploy_assertion_storage.ts similarity index 100% rename from deploy/010_deploy_assertion_storage.ts rename to deploy/011_deploy_assertion_storage.ts diff --git a/deploy/011_deploy_identity_storage_v2.ts b/deploy/012_deploy_identity_storage_v2.ts similarity index 100% rename from deploy/011_deploy_identity_storage_v2.ts rename to deploy/012_deploy_identity_storage_v2.ts diff --git a/deploy/012_deploy_identity_storage.ts b/deploy/013_deploy_identity_storage.ts similarity index 100% rename from deploy/012_deploy_identity_storage.ts rename to deploy/013_deploy_identity_storage.ts diff --git a/deploy/014_deploy_sharding_table_storage_v2.ts b/deploy/014_deploy_sharding_table_storage_v2.ts new file mode 100644 index 00000000..b11b0aaf --- /dev/null +++ b/deploy/014_deploy_sharding_table_storage_v2.ts @@ -0,0 +1,23 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('ShardingTableStorage') && + (hre.helpers.contractDeployments.contracts['ShardingTableStorage'].version === undefined || + hre.helpers.contractDeployments.contracts['ShardingTableStorage'].version?.startsWith('1.')) + ) { + return; + } + + console.log('Deploying ShardingTableStorage V2...'); + + await hre.helpers.deploy({ + newContractName: 'ShardingTableStorageV2', + newContractNameInHub: 'ShardingTableStorage', + }); +}; + +export default func; +func.tags = ['ShardingTableStorageV2', 'v2']; +func.dependencies = ['HubV2']; diff --git a/deploy/015_deploy_sharding_table_storage.ts b/deploy/015_deploy_sharding_table_storage.ts new file mode 100644 index 00000000..3f927fa8 --- /dev/null +++ b/deploy/015_deploy_sharding_table_storage.ts @@ -0,0 +1,22 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('ShardingTableStorage') && + (hre.helpers.contractDeployments.contracts['ShardingTableStorage'].version === undefined || + hre.helpers.contractDeployments.contracts['ShardingTableStorage'].version?.startsWith('2.')) + ) { + return; + } + + console.log('Deploying ShardingTableStorage V1...'); + + await hre.helpers.deploy({ + newContractName: 'ShardingTableStorage', + }); +}; + +export default func; +func.tags = ['ShardingTableStorage', 'v1']; +func.dependencies = ['Hub']; diff --git a/deploy/014_deploy_staking_storage.ts b/deploy/016_deploy_staking_storage.ts similarity index 100% rename from deploy/014_deploy_staking_storage.ts rename to deploy/016_deploy_staking_storage.ts diff --git a/deploy/008_deploy_scoring_proxy.ts b/deploy/017_deploy_node_operator_fee_changes_storage.ts similarity index 64% rename from deploy/008_deploy_scoring_proxy.ts rename to deploy/017_deploy_node_operator_fee_changes_storage.ts index 6940462a..874b79e9 100644 --- a/deploy/008_deploy_scoring_proxy.ts +++ b/deploy/017_deploy_node_operator_fee_changes_storage.ts @@ -3,10 +3,10 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await hre.helpers.deploy({ - newContractName: 'ScoringProxy', + newContractName: 'NodeOperatorFeeChangesStorage', }); }; export default func; -func.tags = ['ScoringProxy', 'v1']; -func.dependencies = ['Hub']; +func.tags = ['NodeOperatorFeeChangesStorage', 'v2']; +func.dependencies = ['HubV2']; diff --git a/deploy/015_deploy_profile_storage.ts b/deploy/018_deploy_profile_storage.ts similarity index 100% rename from deploy/015_deploy_profile_storage.ts rename to deploy/018_deploy_profile_storage.ts diff --git a/deploy/016_deploy_service_agreement_storage_v1.ts b/deploy/019_deploy_service_agreement_storage_v1.ts similarity index 100% rename from deploy/016_deploy_service_agreement_storage_v1.ts rename to deploy/019_deploy_service_agreement_storage_v1.ts diff --git a/deploy/017_deploy_service_agreement_storage_v1u1.ts b/deploy/020_deploy_service_agreement_storage_v1u1.ts similarity index 100% rename from deploy/017_deploy_service_agreement_storage_v1u1.ts rename to deploy/020_deploy_service_agreement_storage_v1u1.ts diff --git a/deploy/018_deploy_service_agreement_storage_proxy.ts b/deploy/021_deploy_service_agreement_storage_proxy.ts similarity index 100% rename from deploy/018_deploy_service_agreement_storage_proxy.ts rename to deploy/021_deploy_service_agreement_storage_proxy.ts diff --git a/deploy/019_deploy_content_asset_storage_v2.ts b/deploy/022_deploy_content_asset_storage_v2.ts similarity index 100% rename from deploy/019_deploy_content_asset_storage_v2.ts rename to deploy/022_deploy_content_asset_storage_v2.ts diff --git a/deploy/020_deploy_content_asset_storage.ts b/deploy/023_deploy_content_asset_storage.ts similarity index 100% rename from deploy/020_deploy_content_asset_storage.ts rename to deploy/023_deploy_content_asset_storage.ts diff --git a/deploy/021_deploy_unfinalized_state_storage.ts b/deploy/024_deploy_unfinalized_state_storage.ts similarity index 100% rename from deploy/021_deploy_unfinalized_state_storage.ts rename to deploy/024_deploy_unfinalized_state_storage.ts diff --git a/deploy/022_deploy_assertion.ts b/deploy/025_deploy_assertion.ts similarity index 100% rename from deploy/022_deploy_assertion.ts rename to deploy/025_deploy_assertion.ts diff --git a/deploy/023_deploy_identity.ts b/deploy/026_deploy_identity.ts similarity index 100% rename from deploy/023_deploy_identity.ts rename to deploy/026_deploy_identity.ts diff --git a/deploy/027_deploy_sharding_table_v2.ts b/deploy/027_deploy_sharding_table_v2.ts new file mode 100644 index 00000000..c57005f8 --- /dev/null +++ b/deploy/027_deploy_sharding_table_v2.ts @@ -0,0 +1,23 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('ShardingTable') && + (hre.helpers.contractDeployments.contracts['ShardingTable'].version === undefined || + hre.helpers.contractDeployments.contracts['ShardingTable'].version?.startsWith('1.')) + ) { + return; + } + + console.log('Deploying ShardingTable V2...'); + + await hre.helpers.deploy({ + newContractName: 'ShardingTableV2', + newContractNameInHub: 'ShardingTable', + }); +}; + +export default func; +func.tags = ['ShardingTableV2', 'v2']; +func.dependencies = ['HubV2', 'ProfileStorage', 'ShardingTableStorageV2', 'StakingStorage']; diff --git a/deploy/024_deploy_sharding_table.ts b/deploy/028_deploy_sharding_table.ts similarity index 57% rename from deploy/024_deploy_sharding_table.ts rename to deploy/028_deploy_sharding_table.ts index 5f957641..9650f41b 100644 --- a/deploy/024_deploy_sharding_table.ts +++ b/deploy/028_deploy_sharding_table.ts @@ -2,6 +2,16 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('ShardingTable') && + (hre.helpers.contractDeployments.contracts['ShardingTable'].version === undefined || + hre.helpers.contractDeployments.contracts['ShardingTable'].version?.startsWith('2.')) + ) { + return; + } + + console.log('Deploying ShardingTable V1...'); + await hre.helpers.deploy({ newContractName: 'ShardingTable', }); diff --git a/deploy/029_deploy_staking_v2.ts b/deploy/029_deploy_staking_v2.ts new file mode 100644 index 00000000..8cd1bdf7 --- /dev/null +++ b/deploy/029_deploy_staking_v2.ts @@ -0,0 +1,33 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('Staking') && + (hre.helpers.contractDeployments.contracts['Staking'].version === undefined || + hre.helpers.contractDeployments.contracts['Staking'].version?.startsWith('1.')) + ) { + return; + } + + console.log('Deploying Staking V2...'); + + await hre.helpers.deploy({ + newContractName: 'StakingV2', + newContractNameInHub: 'Staking', + }); +}; + +export default func; +func.tags = ['StakingV2', 'v2']; +func.dependencies = [ + 'HubV2', + 'ShardingTableV2', + 'IdentityStorageV2', + 'ParametersStorage', + 'ProfileStorage', + 'ServiceAgreementStorageProxy', + 'ShardingTableStorageV2', + 'StakingStorage', + 'NodeOperatorFeeChangesStorage', +]; diff --git a/deploy/025_deploy_staking.ts b/deploy/030_deploy_staking.ts similarity index 64% rename from deploy/025_deploy_staking.ts rename to deploy/030_deploy_staking.ts index 0b73e287..16c836d3 100644 --- a/deploy/025_deploy_staking.ts +++ b/deploy/030_deploy_staking.ts @@ -2,6 +2,16 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('Staking') && + (hre.helpers.contractDeployments.contracts['Staking'].version === undefined || + hre.helpers.contractDeployments.contracts['Staking'].version?.startsWith('2.')) + ) { + return; + } + + console.log('Deploying Staking V1...'); + await hre.helpers.deploy({ newContractName: 'Staking', }); diff --git a/deploy/026_deploy_profile.ts b/deploy/031_deploy_profile.ts similarity index 100% rename from deploy/026_deploy_profile.ts rename to deploy/031_deploy_profile.ts diff --git a/deploy/032_deploy_commit_manager_v2.ts b/deploy/032_deploy_commit_manager_v2.ts new file mode 100644 index 00000000..c342e54d --- /dev/null +++ b/deploy/032_deploy_commit_manager_v2.ts @@ -0,0 +1,39 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('CommitManagerV1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1'].version?.startsWith('1.')) + ) { + return; + } + + console.log('Deploying CommitManager V2...'); + + const CommitManagerV2 = await hre.helpers.deploy({ + newContractName: 'CommitManagerV2', + newContractNameInHub: 'CommitManagerV1', + }); + + await hre.helpers.updateContractParameters('CommitManagerV1', CommitManagerV2); +}; + +export default func; +func.tags = ['CommitManagerV2', 'v2']; +func.dependencies = [ + 'HubV2', + 'IdentityStorageV2', + 'ProximityScoringProxy', + 'Log2PLDSF', + 'LinearSum', + 'ParametersStorage', + 'ProfileStorage', + 'ServiceAgreementStorageProxy', + 'HashingProxy', + 'SHA256', + 'ShardingTableStorageV2', + 'StakingV2', + 'StakingStorage', +]; diff --git a/deploy/027_deploy_commit_manager_v1.ts b/deploy/033_deploy_commit_manager_v1.ts similarity index 68% rename from deploy/027_deploy_commit_manager_v1.ts rename to deploy/033_deploy_commit_manager_v1.ts index e9d34d46..98714077 100644 --- a/deploy/027_deploy_commit_manager_v1.ts +++ b/deploy/033_deploy_commit_manager_v1.ts @@ -2,6 +2,16 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('CommitManagerV1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1'].version?.startsWith('2.')) + ) { + return; + } + + console.log('Deploying CommitManager V1...'); + const CommitManagerV1 = await hre.helpers.deploy({ newContractName: 'CommitManagerV1', }); diff --git a/deploy/034_deploy_commit_manager_v2_u1.ts b/deploy/034_deploy_commit_manager_v2_u1.ts new file mode 100644 index 00000000..81f28a89 --- /dev/null +++ b/deploy/034_deploy_commit_manager_v2_u1.ts @@ -0,0 +1,41 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('CommitManagerV1U1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version?.startsWith('1.')) + ) { + return; + } + + console.log('Deploying CommitManager V2U1...'); + + const CommitManagerV2U1 = await hre.helpers.deploy({ + newContractName: 'CommitManagerV2U1', + newContractNameInHub: 'CommitManagerV1U1', + }); + + await hre.helpers.updateContractParameters('CommitManagerV1U1', CommitManagerV2U1); +}; + +export default func; +func.tags = ['CommitManagerV2U1', 'v2']; +func.dependencies = [ + 'HubV2', + 'IdentityStorageV2', + 'ProximityScoringProxy', + 'Log2PLDSF', + 'LinearSum', + 'ParametersStorage', + 'ProfileStorage', + 'ServiceAgreementStorageProxy', + 'ContentAssetStorageV2', + 'HashingProxy', + 'SHA256', + 'ShardingTableStorageV2', + 'StakingV2', + 'StakingStorageV2', + 'UnfinalizedStateStorage', +]; diff --git a/deploy/029_deploy_commit_manager_v1_u1.ts b/deploy/035_deploy_commit_manager_v1_u1.ts similarity index 70% rename from deploy/029_deploy_commit_manager_v1_u1.ts rename to deploy/035_deploy_commit_manager_v1_u1.ts index a99b3a8c..ea72eb4d 100644 --- a/deploy/029_deploy_commit_manager_v1_u1.ts +++ b/deploy/035_deploy_commit_manager_v1_u1.ts @@ -2,6 +2,16 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + if ( + hre.helpers.isDeployed('CommitManagerV1U1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version?.startsWith('2.')) + ) { + return; + } + + console.log('Deploying CommitManager V1U1...'); + const CommitManagerV1U1 = await hre.helpers.deploy({ newContractName: 'CommitManagerV1U1', }); @@ -12,7 +22,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { export default func; func.tags = ['CommitManagerV1U1', 'v1']; func.dependencies = [ - 'ContentAssetStorage', 'Hub', 'IdentityStorage', 'ScoringProxy', @@ -20,6 +29,7 @@ func.dependencies = [ 'ParametersStorage', 'ProfileStorage', 'ServiceAgreementStorageProxy', + 'ContentAssetStorage', 'HashingProxy', 'SHA256', 'ShardingTableStorage', diff --git a/deploy/028_deploy_proof_manager_v1.ts b/deploy/036_deploy_proof_manager_v1.ts similarity index 100% rename from deploy/028_deploy_proof_manager_v1.ts rename to deploy/036_deploy_proof_manager_v1.ts diff --git a/deploy/030_deploy_proof_manager_v1_u1.ts b/deploy/037_deploy_proof_manager_v1_u1.ts similarity index 100% rename from deploy/030_deploy_proof_manager_v1_u1.ts rename to deploy/037_deploy_proof_manager_v1_u1.ts diff --git a/deploy/031_deploy_service_agreement_v1.ts b/deploy/038_deploy_service_agreement_v1.ts similarity index 100% rename from deploy/031_deploy_service_agreement_v1.ts rename to deploy/038_deploy_service_agreement_v1.ts diff --git a/deploy/032_deploy_content_asset.ts b/deploy/039_deploy_content_asset.ts similarity index 100% rename from deploy/032_deploy_content_asset.ts rename to deploy/039_deploy_content_asset.ts diff --git a/deployments/gnosis_chiado_dev_contracts.json b/deployments/gnosis_chiado_dev_contracts.json index aaf34a1f..279d7261 100644 --- a/deployments/gnosis_chiado_dev_contracts.json +++ b/deployments/gnosis_chiado_dev_contracts.json @@ -1,12 +1,12 @@ { "contracts": { "Assertion": { - "deployed": true, - "deploymentTimestamp": 1701108252805, - "evmAddress": "0x2785dB031C67B17B07daB7E1e3F9690E9709B735", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x3310E685a5471e9824d28AEeAce65d61F5e53698", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798862801, + "deployed": true }, "AssertionStorage": { "deployed": true, @@ -17,28 +17,28 @@ "version": "1.0.0" }, "CommitManagerV1": { - "deployed": true, - "deploymentTimestamp": 1701108327533, - "evmAddress": "0x94218Ce6A441f027dFB08d0cAEe3A028014A70C2", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0xD8934457A0dFF97b059E3370AEd30F280838f1E6", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798918693, + "deployed": true }, "CommitManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1701108343997, - "evmAddress": "0x223A884F8a4CA10242aAC2b5a5857337764Ff805", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x786a9494a031D6221Ad4246fDcFf1dbA0CDb48fE", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798926032, + "deployed": true }, "ContentAsset": { - "deployed": true, - "deploymentTimestamp": 1701108698623, - "evmAddress": "0xaC93381697288fBBa9C4a7ED555ca57C74f0e85B", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0x0B4bf3f3c83c0d44368d3da175d47BB582EE19ad", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798985649, + "deployed": true }, "ContentAssetStorage": { "deployed": true, @@ -49,12 +49,12 @@ "version": "2.0.0" }, "HashingProxy": { - "deployed": true, - "deploymentTimestamp": 1701108085609, - "evmAddress": "0xD1a30E13DefA65684e22CC09EF51ca12D9c022C3", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x222403e49651Fe40Fa914627AEEF89CDF7040c39", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798758112, + "deployed": true }, "Hub": { "deployed": true, @@ -73,91 +73,99 @@ "version": "1.0.1" }, "Identity": { - "deployed": true, - "deploymentTimestamp": 1701108258606, - "evmAddress": "0x654c24F865cd6420bEA563DE61ccf4b1C25C5EC0", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0xB7203c97A4d9a9482fa741d2Ce81d5E7B7B64701", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798873457, + "deployed": true }, "IdentityStorage": { - "deployed": true, - "deploymentTimestamp": 1701108151627, - "evmAddress": "0xf0c302F9F7DC143BedF575ad9a3697194FF1bdCf", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "2.0.0" + "evmAddress": "0x935baC9BF1F6a593676d84123790646DC9987377", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798809311, + "deployed": true + }, + "LinearSum": { + "evmAddress": "0x50c8Bcea7935242b991934Eb0251ff0CdAD4ED7f", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "3170df6307d65977d66d8bc22134ecaf5fef8fe5", + "deploymentTimestamp": 1706896901040, + "deployed": true }, "Log2PLDSF": { - "deployed": true, - "deploymentTimestamp": 1701108123571, - "evmAddress": "0x144D304afA78Fd7093c2257695D1633cCEfaf13E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": null + "evmAddress": "0x456E9ad2c7DE41a206d8f3d4C55790666e3438D8", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798780791, + "deployed": true }, "ParametersStorage": { - "deployed": true, - "deploymentTimestamp": 1701108052661, - "evmAddress": "0x4dD9339bBe3C49CF8806b7440cbc57BFC82b5ad5", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.1.0" + "evmAddress": "0x95637cdd9b5a9442343Bd77c1A8be4a9090AdB1e", + "version": "1.1.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798743324, + "deployed": true }, "Profile": { - "deployed": true, - "deploymentTimestamp": 1701108296922, - "evmAddress": "0xC7967b6dF41CD6A331D7Bb88129c63b6bb91614e", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0x1C0245DC80fe287B5Ac6a6a1E85ec230a56cc5ea", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798903277, + "deployed": true }, "ProfileStorage": { - "deployed": true, - "deploymentTimestamp": 1701108187258, - "evmAddress": "0xD658511427E6B4BeCcea024832F891e398A40343", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "evmAddress": "0xe70Fd17F76Eed679Ca8302f059ACf5551694f2fe", + "version": "1.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798853108, + "deployed": true }, "ProofManagerV1": { - "deployed": true, - "deploymentTimestamp": 1701108333806, - "evmAddress": "0x1E83a78E12C693d0f2EB94AC51e442E0D1B63a3b", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xac1C4Df4B96A84d31d004Bd77DCc1ace853bbfdd", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798933283, + "deployed": true }, "ProofManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1701108370005, - "evmAddress": "0xD77E59D58a3020f2752B9003151B8dF22588d60F", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xaaDd33785d8bc2317910a010CFDf0792e4f54A83", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798952557, + "deployed": true }, "SHA256": { - "deployed": true, - "deploymentTimestamp": 1701108091443, - "evmAddress": "0xfE6f7Bb8Dc73b35F14C3bE12bCA424B5DA6C8d94", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": null + "evmAddress": "0x00c419eb97fCd4bBe0a9730A629b842288C52E33", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798764437, + "deployed": true }, "ScoringProxy": { - "deployed": true, - "deploymentTimestamp": 1701108109320, - "evmAddress": "0x57A5FcB27C97d49578d67e2f276A82B7F61e3Af9", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x55203C4220650Ee63F544a0EB2Fad8a773319576", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798775189, + "deployed": true }, "ServiceAgreementStorageProxy": { "deployed": true, - "deploymentTimestamp": 1701108223122, - "evmAddress": "0x36bE35DD8217ebb593D3507E49A7415592dAeD01", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634863827, + "evmAddress": "0xF55bB7410Da447bde24b9996A8Dd23915B597670", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.0" }, "ServiceAgreementStorageV1": { @@ -177,44 +185,44 @@ "version": "1.0.0" }, "ServiceAgreementV1": { - "deployed": true, - "deploymentTimestamp": 1701108691794, - "evmAddress": "0x7e37087595f31a15c3BFc325104e3FaE08ac730f", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.1.1" + "evmAddress": "0x7C9176EBA46Be9AB324474598e2788DEEcE11A7F", + "version": "1.1.1", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798967276, + "deployed": true }, "ShardingTable": { - "deployed": true, - "deploymentTimestamp": 1701108272713, - "evmAddress": "0xBBd73E52Bc29678c7b4Df53F50162BB0cAc5D7Db", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x05d9631984af59791E81e2c8213D095181c58d26", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798879374, + "deployed": true }, "ShardingTableStorage": { - "deployed": true, - "deploymentTimestamp": 1701108162127, - "evmAddress": "0x42232C6A2C4230B56c5DDc3536aaaFa4F6f97E2E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "evmAddress": "0x9E55a883EB23135D04348CaF7fF5d57A9B06a176", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798819331, + "deployed": true }, "Staking": { - "deployed": true, - "deploymentTimestamp": 1701108286747, - "evmAddress": "0xf73bd80B7B05575DDd79247eEA644E030094a47B", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xE4B3cf42BF9F4D796768deFa5B572C527dFe8C7f", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798889242, + "deployed": true }, "StakingStorage": { - "deployed": true, - "deploymentTimestamp": 1701108180122, - "evmAddress": "0x5dC1195A8AFA9b4Ba86D2BaF6908897031d70195", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "evmAddress": "0x755DC8bBc61e907c2c1Cc1eD00da90a1e6945B69", + "version": "1.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798833192, + "deployed": true }, "Token": { "deployed": true, @@ -235,6 +243,14 @@ "gitBranch": "main", "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", "version": "1.0.0" + }, + "NodeOperatorFeeChangesStorage": { + "evmAddress": "0x9451dCF84ad47159E5D5ec8E39384821c3Add199", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798843205, + "deployed": true } } } diff --git a/deployments/gnosis_chiado_test_contracts.json b/deployments/gnosis_chiado_test_contracts.json index a88f6109..f221be7f 100644 --- a/deployments/gnosis_chiado_test_contracts.json +++ b/deployments/gnosis_chiado_test_contracts.json @@ -1,12 +1,12 @@ { "contracts": { "Assertion": { - "deployed": true, - "deploymentTimestamp": 1701109048030, - "evmAddress": "0x9B47F7AD155b52A88968c25DA9Eff080c17B4AC5", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x4c33eEB7A07e7602461F23701E3D144587174aCA", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868368522, + "deployed": true }, "AssertionStorage": { "deployed": true, @@ -17,28 +17,28 @@ "version": "1.0.0" }, "CommitManagerV1": { - "deployed": true, - "deploymentTimestamp": 1701110482942, - "evmAddress": "0x2E714e7803179C05117bdbb12c6ee90582c24582", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x49AD6Bcca1F56EF36Eb5d5EF27e45368991A8469", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868423365, + "deployed": true }, "CommitManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1701110508065, - "evmAddress": "0xb917975CbBD80aa54fDf56A9Eb51bEcB2Dd29F0d", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0xc9487464de31BE37fEe2DFc890115f2d6DEa6aA3", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868435083, + "deployed": true }, "ContentAsset": { - "deployed": true, - "deploymentTimestamp": 1701110543790, - "evmAddress": "0xc200F6E6482c66D80462cD1581D1649B97036d47", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xdBA79D8908DA516E2b6c5756A88CE94e36cDBB99", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868468460, + "deployed": true }, "ContentAssetStorage": { "deployed": true, @@ -49,12 +49,12 @@ "version": "2.0.1" }, "HashingProxy": { - "deployed": true, - "deploymentTimestamp": 1701108844930, - "evmAddress": "0x887DED2AC5bcD8e4A6280f9e6E8baE4DC22DAe3C", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x80c36249EC7bBEc58801513Ce8eF2ef09fC48282", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868293682, + "deployed": true }, "Hub": { "deployed": true, @@ -73,84 +73,84 @@ "version": "1.0.1" }, "Identity": { - "deployed": true, - "deploymentTimestamp": 1701110417798, - "evmAddress": "0x2CAf5c6c64b5043B74ad91681D926a49eE4729d1", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x0bD5C48D45fC4ac5f94A045BdB3F15c98AD46912", + "version": "1.0.1", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868380049, + "deployed": true }, "IdentityStorage": { - "deployed": true, - "deploymentTimestamp": 1701108916913, - "evmAddress": "0x578a7C9C5184aa3773cd9a8fa90672e2525452C7", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "2.0.0" + "evmAddress": "0xbd8fAF8bddF105d8628EFF819C08200D8E7eD4E9", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868335069, + "deployed": true }, "Log2PLDSF": { - "deployed": true, - "deploymentTimestamp": 1702464453332, - "evmAddress": "0xe2f70b7168EBeC9ff8Fe51219Da5c89E5846e6e4", - "gitBranch": "main", - "gitCommitHash": "24d3e2392c0bb6a6164533219a84cd216b46a6a5", - "version": null + "evmAddress": "0xCC8439a7B8C72792Bf0B2EcF9d674B411C262b2C", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868313811, + "deployed": true }, "ParametersStorage": { - "deployed": true, - "deploymentTimestamp": 1701108824845, - "evmAddress": "0x14C082622F1A9cb7390B3C35eE264b7844416E0E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.1.0" + "evmAddress": "0x3119010DFA3cd4db7e40f19e8511e3834Be34E5e", + "version": "1.1.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868279119, + "deployed": true }, "Profile": { - "deployed": true, - "deploymentTimestamp": 1701110472907, - "evmAddress": "0x4AbC244E716624239EaBF4d77E29966C925ff67E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0x9e9c9c0eC92909660126d3f25d03F1991E7B8d67", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868404817, + "deployed": true }, "ProfileStorage": { - "deployed": true, - "deploymentTimestamp": 1701108962846, - "evmAddress": "0x14102863b17b88ffF4823AC364a46EDE59A67327", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "evmAddress": "0xa936Bb6fF11D69e6412ABF79b29ed1eEccBD181d", + "version": "1.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868361747, + "deployed": true }, "ProofManagerV1": { - "deployed": true, - "deploymentTimestamp": 1701110497159, - "evmAddress": "0x058205a0c7173Db68a67Fdb68646880bd3B131CE", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0x26AFd1107a5DFB0BFbE3db7Ea5E65F458ce5421c", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868443207, + "deployed": true }, "ProofManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1701110523131, - "evmAddress": "0xD5E8Cf058eF61948A3FBD990998ED9CC2e57Cae8", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xF34316A774144A5f3Cd52B9f894805A7E6c44881", + "version": "1.0.2", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868454555, + "deployed": true }, "SHA256": { - "deployed": true, - "deploymentTimestamp": 1701108851159, - "evmAddress": "0x722d696CAa2558A3292E26E5B2Ce7e73DB046674", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": null + "evmAddress": "0x60ffA5428B4EC7c26c821E985FFaa6981F289C7b", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868300145, + "deployed": true }, "ScoringProxy": { - "deployed": true, - "deploymentTimestamp": 1701108858436, - "evmAddress": "0xc2BB756BBD69FdF313044BF2deB5CE363b4377D4", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x44F8C90103645E55640B185F52E3ef46287C10F1", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868307300, + "deployed": true }, "ServiceAgreementStorageProxy": { "deployed": true, @@ -177,36 +177,36 @@ "version": "1.0.0" }, "ServiceAgreementV1": { - "deployed": true, - "deploymentTimestamp": 1701110533415, - "evmAddress": "0x5928e91D661D2cE54a4236236c2AF17f3705d48E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.1.1" + "evmAddress": "0x15bd010E523B49d32fefA1702A82DA509DC8474A", + "version": "1.1.1", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868462261, + "deployed": true }, "ShardingTable": { - "deployed": true, - "deploymentTimestamp": 1701110442176, - "evmAddress": "0xf9D7743D56E090Ed8AcD6E2cFE5BD19F5E4bcBf2", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "evmAddress": "0x58Aaf029Bf04644418CB16a15382109A43aBDAA2", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868386856, + "deployed": true }, "ShardingTableStorage": { - "deployed": true, - "deploymentTimestamp": 1701108935108, - "evmAddress": "0x11E2b30aA6a3F92a8aCB9Bfc181cd164c155ff9B", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "evmAddress": "0xE3d940FE74b2071Ed849a6d405A0a3C57F699DdA", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868343606, + "deployed": true }, "Staking": { - "deployed": true, - "deploymentTimestamp": 1701110465761, - "evmAddress": "0x2F8ac161E4B361506f948B0FE0099EFB0323f7ba", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "evmAddress": "0xf65254BEb90a070898237DD8FEA212363D4ade52", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868394091, + "deployed": true }, "StakingStorage": { "deployed": true, @@ -235,6 +235,22 @@ "gitBranch": "main", "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", "version": "1.0.0" + }, + "LinearSum": { + "evmAddress": "0x669C7e51EbBdA5C57503Ff19feb463109874C2D5", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "3170df6307d65977d66d8bc22134ecaf5fef8fe5", + "deploymentTimestamp": 1706896932563, + "deployed": true + }, + "NodeOperatorFeeChangesStorage": { + "evmAddress": "0x30d65d3fB94b645CEd19c561F60124B83ea8B891", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868354605, + "deployed": true } } } diff --git a/deployments/parameters.json b/deployments/parameters.json index 84c48e8d..0db5c951 100644 --- a/deployments/parameters.json +++ b/deployments/parameters.json @@ -68,6 +68,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -85,7 +91,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -93,9 +99,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { @@ -224,6 +230,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -241,7 +253,7 @@ "epochLength": "3600", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -249,9 +261,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "600" }, "ProofManagerV1": { @@ -380,6 +392,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -397,7 +415,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -405,9 +423,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { @@ -536,6 +554,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -553,7 +577,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -561,9 +585,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "2419200", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "3600", + "stakeWithdrawalDelay": "2419200", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { diff --git a/hardhat.config.ts b/hardhat.config.ts index 826fe7c2..1c39f33f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -100,6 +100,7 @@ config.mocha = { reporterOptions: { excludeContracts: [], }, + timeout: 100000000, }; config.abiExporter = { @@ -122,9 +123,15 @@ config.abiExporter = { 'IERC734Extended.sol', 'IERC4906.sol', 'Ownable.sol', + 'CommitManagerErrorsV2.sol', 'ContentAssetErrors.sol', + 'ProfileErrors.sol', 'ServiceAgreementErrorsV1.sol', 'ServiceAgreementErrorsV1U1.sol', + 'ServiceAgreementErrorsV2.sol', + 'ShardingTableErrors.sol', + 'StakingErrors.sol', + 'TokenErrors.sol', 'Shares.sol', ], spacing: 2, diff --git a/hardhat.node.config.ts b/hardhat.node.config.ts index d9b15ccd..5e8da7d2 100644 --- a/hardhat.node.config.ts +++ b/hardhat.node.config.ts @@ -5,7 +5,7 @@ import { lazyObject } from 'hardhat/plugins'; import { HardhatRuntimeEnvironment, HardhatUserConfig } from 'hardhat/types'; import { Helpers } from './utils/helpers'; -import { accounts, rpc } from './utils/network'; +import { rpc } from './utils/network'; extendEnvironment((hre: HardhatRuntimeEnvironment) => { hre.helpers = lazyObject(() => new Helpers(hre)); @@ -21,7 +21,6 @@ const config: HardhatUserConfig = { localhost: { environment: 'development', url: rpc('localhost'), - accounts: accounts('localhost'), saveDeployments: false, }, hardhat: { @@ -31,6 +30,7 @@ const config: HardhatUserConfig = { gasMultiplier: 1, blockGasLimit: 30_000_000, hardfork: 'shanghai', + accounts: { count: 200 }, throwOnTransactionFailures: true, throwOnCallFailures: true, loggingEnabled: false, diff --git a/index.ts b/index.ts index 70660d08..e5b25ce9 100644 --- a/index.ts +++ b/index.ts @@ -3,6 +3,8 @@ import Assertion from './abi/Assertion.json'; import AssertionStorage from './abi/AssertionStorage.json'; import CommitManagerV1 from './abi/CommitManagerV1.json'; import CommitManagerV1U1 from './abi/CommitManagerV1U1.json'; +import CommitManagerV2 from './abi/CommitManagerV2.json'; +import CommitManagerV2U1 from './abi/CommitManagerV2U1.json'; import ContentAsset from './abi/ContentAsset.json'; import ContentAssetStorage from './abi/ContentAssetStorage.json'; import ContentAssetStorageV2 from './abi/ContentAssetStorageV2.json'; @@ -20,16 +22,18 @@ import IdentityStorageV2 from './abi/IdentityStorageV2.json'; import IHashFunction from './abi/IHashFunction.json'; import Indexable from './abi/Indexable.json'; import Initializable from './abi/Initializable.json'; -import IScoreFunction from './abi/IScoreFunction.json'; +import IProximityScoreFunctionsPair from './abi/IProximityScoreFunctionsPair.json'; +import LinearSum from './abi/LinearSum.json'; import Log2PLDSF from './abi/Log2PLDSF.json'; import MultiSigWallet from './abi/MultiSigWallet.json'; import Named from './abi/Named.json'; +import NodeOperatorFeeChangesStorage from './abi/NodeOperatorFeeChangesStorage.json'; import ParametersStorage from './abi/ParametersStorage.json'; import Profile from './abi/Profile.json'; import ProfileStorage from './abi/ProfileStorage.json'; import ProofManagerV1 from './abi/ProofManagerV1.json'; import ProofManagerV1U1 from './abi/ProofManagerV1U1.json'; -import ScoringProxy from './abi/ScoringProxy.json'; +import ProximityScoringProxy from './abi/ProximityScoringProxy.json'; import ServiceAgreementStorageProxy from './abi/ServiceAgreementStorageProxy.json'; import ServiceAgreementStorageV1 from './abi/ServiceAgreementStorageV1.json'; import ServiceAgreementStorageV1U1 from './abi/ServiceAgreementStorageV1U1.json'; @@ -37,8 +41,11 @@ import ServiceAgreementV1 from './abi/ServiceAgreementV1.json'; import SHA256 from './abi/SHA256.json'; import ShardingTable from './abi/ShardingTable.json'; import ShardingTableStorage from './abi/ShardingTableStorage.json'; +import ShardingTableStorageV2 from './abi/ShardingTableStorageV2.json'; +import ShardingTableV2 from './abi/ShardingTableV2.json'; import Staking from './abi/Staking.json'; import StakingStorage from './abi/StakingStorage.json'; +import StakingV2 from './abi/StakingV2.json'; import Token from './abi/Token.json'; import UnfinalizedStateStorage from './abi/UnfinalizedStateStorage.json'; import Versioned from './abi/Versioned.json'; @@ -49,7 +56,7 @@ const ABIV1 = { HubController, ParametersStorage, HashingProxy, - ScoringProxy, + ScoringProxy: ProximityScoringProxy, SHA256, ShardingTableStorage, ShardingTable, @@ -68,6 +75,7 @@ const ABIV1 = { Token, IdentityStorage, Identity, + LinearSum, Log2PLDSF, ProfileStorage, Profile, @@ -84,7 +92,7 @@ const ABIV1 = { IHashFunction, Indexable, Initializable, - IScoreFunction, + IScoreFunction: IProximityScoreFunctionsPair, Named, Versioned, }; @@ -93,6 +101,12 @@ const ABIV2 = { HubV2, ContentAssetStorageV2, IdentityStorageV2, + CommitManagerV2, + CommitManagerV2U1, + NodeOperatorFeeChangesStorage, + ShardingTableV2, + ShardingTableStorageV2, + StakingV2, }; export { @@ -100,18 +114,18 @@ export { HubController as HubControllerABI, ParametersStorage as ParametersStorageABI, HashingProxy as HashingProxyABI, - ScoringProxy as ScoringProxyABI, + ProximityScoringProxy as ScoringProxyABI, SHA256 as SHA256ABI, - ShardingTableStorage as ShardingTableStorageABI, - ShardingTable as ShardingTableABI, + ShardingTableStorageV2 as ShardingTableStorageABI, + ShardingTableV2 as ShardingTableABI, AssertionStorage as AssertionStorageABI, Assertion as AssertionABI, ServiceAgreementStorageV1 as ServiceAgreementStorageV1ABI, ServiceAgreementStorageV1U1 as ServiceAgreementStorageV1U1ABI, ServiceAgreementStorageProxy as ServiceAgreementStorageProxyABI, ServiceAgreementV1 as ServiceAgreementV1ABI, - CommitManagerV1 as CommitManagerV1ABI, - CommitManagerV1U1 as CommitManagerV1U1ABI, + CommitManagerV2 as CommitManagerV1ABI, + CommitManagerV2U1 as CommitManagerV1U1ABI, ProofManagerV1 as ProofManagerV1ABI, ProofManagerV1U1 as ProofManagerV1U1ABI, ContentAssetStorageV2 as ContentAssetStorageABI, @@ -119,11 +133,13 @@ export { Token as TokenABI, IdentityStorageV2 as IdentityStorageABI, Identity as IdentityABI, + LinearSum as LinearSumABI, Log2PLDSF as Log2PLDSFABI, + NodeOperatorFeeChangesStorage as NodeOperatorFeeChangesStorageABI, ProfileStorage as ProfileStorageABI, Profile as ProfileABI, StakingStorage as StakingStorageABI, - Staking as StakingABI, + StakingV2 as StakingABI, UnfinalizedStateStorage as UnfinalizedStateStorageABI, WhitelistStorage as WhitelistStorageABI, MultiSigWallet as MultiSigWalletABI, @@ -135,7 +151,7 @@ export { IHashFunction as IHashFunctionABI, Indexable as IndexableABI, Initializable as InitializableABI, - IScoreFunction as IScoreFunctionABI, + IProximityScoreFunctionsPair as IScoreFunctionABI, Named as NamedABI, Versioned as VersionedABI, ABIV1, diff --git a/package-lock.json b/package-lock.json index 4713dc92..cc2d9e5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dkg-evm-module", - "version": "4.1.1", + "version": "4.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dkg-evm-module", - "version": "4.1.1", + "version": "4.2.0", "license": "Apache-2.0", "dependencies": { "@openzeppelin/contracts": "^4.9.3", diff --git a/package.json b/package.json index 8e2de2d7..91123df2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dkg-evm-module", - "version": "4.1.1", + "version": "4.2.0", "description": "Smart contracts for OriginTrail V6", "main": "index.ts", "files": [ @@ -121,6 +121,7 @@ "test:gas:trace": "cross-env GAS_REPORT=true hardhat test --network hardhat --trace", "test:gas": "cross-env GAS_REPORT=true hardhat test --network hardhat", "test:integration": "hardhat test --network hardhat --grep '@integration'", + "test:parallel": "hardhat test --network hardhat --parallel", "test:trace": "hardhat test --network hardhat --trace", "test:unit": "hardhat test --network hardhat --grep '@unit'", "test:v1:integration": "hardhat test --network hardhat --grep '@v1 @integration'", diff --git a/test/v1/unit/CommitManagerV1.test.ts b/test/v1/unit/CommitManagerV1.test.ts index 35b6776c..97620cf4 100644 --- a/test/v1/unit/CommitManagerV1.test.ts +++ b/test/v1/unit/CommitManagerV1.test.ts @@ -111,8 +111,8 @@ describe('@v1 @unit CommitManagerV1 contract', function () { expect(await CommitManagerV1.name()).to.equal('CommitManagerV1'); }); - it('The contract is version "1.0.1"', async () => { - expect(await CommitManagerV1.version()).to.equal('1.0.1'); + it('The contract is version "1.0.2"', async () => { + expect(await CommitManagerV1.version()).to.equal('1.0.2'); }); it('Create new asset, check if commit window is open, expect to be true', async () => { diff --git a/test/v1/unit/CommitManagerV1U1.test.ts b/test/v1/unit/CommitManagerV1U1.test.ts index 9fa37a01..240e937c 100644 --- a/test/v1/unit/CommitManagerV1U1.test.ts +++ b/test/v1/unit/CommitManagerV1U1.test.ts @@ -160,8 +160,8 @@ describe('@v1 @unit CommitManagerV1U1 contract', function () { expect(await CommitManagerV1U1.name()).to.equal('CommitManagerV1U1'); }); - it('The contract is version "1.0.1"', async () => { - expect(await CommitManagerV1U1.version()).to.equal('1.0.1'); + it('The contract is version "1.0.2"', async () => { + expect(await CommitManagerV1U1.version()).to.equal('1.0.2'); }); it('Create new asset, update and finalize update, check if commit window is open, expect to be true', async () => { diff --git a/test/v1/unit/ParametersStorage.test.ts b/test/v1/unit/ParametersStorage.test.ts index 3546d0ca..f0d7e17a 100644 --- a/test/v1/unit/ParametersStorage.test.ts +++ b/test/v1/unit/ParametersStorage.test.ts @@ -310,7 +310,7 @@ describe('@v1 @unit ParametersStorage contract', function () { }); it('validate stake withdrawal delay for owner, expect to pass', async () => { - const valueInContract = 5; + const valueInContract = 1; const newValue = '7'; stakeWithdrawalDelay = await ParametersStorage.stakeWithdrawalDelay(); const expectedValue = `${stakeWithdrawalDelay}/60`; @@ -336,7 +336,7 @@ describe('@v1 @unit ParametersStorage contract', function () { }); it('validate reward withdrawal delay for owner, expect to pass', async () => { - const valueInContract = 5; + const valueInContract = 1; const newValue = '7'; rewardWithdrawalDelay = await ParametersStorage.rewardWithdrawalDelay(); const expectedValue = `${rewardWithdrawalDelay}/60`; diff --git a/test/v1/unit/Profile.test.ts b/test/v1/unit/Profile.test.ts index 7c18d62c..3bed8777 100644 --- a/test/v1/unit/Profile.test.ts +++ b/test/v1/unit/Profile.test.ts @@ -28,9 +28,10 @@ describe('@v1 @unit Profile contract', function () { const identityId1 = 1; async function createProfile() { - await expect(Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN')) - .to.emit(Profile, 'ProfileCreated') - .withArgs(identityId1, nodeId1); + await expect(Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN')).to.emit( + Profile, + 'ProfileCreated', + ); } async function deployProfileFixture(): Promise { @@ -56,8 +57,8 @@ describe('@v1 @unit Profile contract', function () { expect(await Profile.name()).to.equal('Profile'); }); - it('The contract is version "1.0.2"', async () => { - expect(await Profile.version()).to.equal('1.0.2'); + it('The contract is version "1.0.3"', async () => { + expect(await Profile.version()).to.equal('1.0.3'); }); it('Create a profile with whitelisted node, expect to pass', async () => { diff --git a/test/v1/unit/ShardingTableStorage.test.ts b/test/v1/unit/ShardingTableStorage.test.ts index c6fe2acc..b1e582d5 100644 --- a/test/v1/unit/ShardingTableStorage.test.ts +++ b/test/v1/unit/ShardingTableStorage.test.ts @@ -44,8 +44,8 @@ describe('@v1 @unit ShardingTableStorage Contract', function () { } async function createMultipleProfiles() { - const adminWallet1 = await Profile.connect(accounts[1]); - const adminWallet2 = await Profile.connect(accounts[2]); + const adminWallet1 = Profile.connect(accounts[1]); + const adminWallet2 = Profile.connect(accounts[2]); const profile1 = await Profile.createProfile(accounts[3].address, nodeId1, 'Token', 'TKN'); const profile2 = await adminWallet1.createProfile(accounts[4].address, nodeId2, 'Token1', 'TKN1'); const profile3 = await adminWallet2.createProfile(accounts[5].address, nodeId3, 'Token2', 'TKN2'); diff --git a/test/v1/unit/Staking.test.ts b/test/v1/unit/Staking.test.ts index 237ec52c..a4f08974 100644 --- a/test/v1/unit/Staking.test.ts +++ b/test/v1/unit/Staking.test.ts @@ -131,7 +131,7 @@ describe('@v1 @unit Staking contract', function () { }); it('Contract should be able to transferStake; expect to pass', async () => { - await Token.mint(StakingStorage.address, hre.ethers.utils.parseEther(`${5_000_000}`)); + await Token.mint(StakingStorage.address, hre.ethers.utils.parseEther(`${2_000_000}`)); const initialReceiverBalance = await Token.balanceOf(accounts[1].address); await StakingStorage.transferStake(accounts[1].address, transferAmount); @@ -140,7 +140,7 @@ describe('@v1 @unit Staking contract', function () { }); it('Create 1 node; expect that stake is created and correctly set', async () => { - await Token.increaseAllowance(Staking.address, hre.ethers.utils.parseEther(`${5_000_000}`)); + await Token.increaseAllowance(Staking.address, hre.ethers.utils.parseEther(`${2_000_000}`)); const nodeId1 = '0x07f38512786964d9e70453371e7c98975d284100d44bd68dab67fe00b525cb66'; await Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN'); @@ -148,16 +148,16 @@ describe('@v1 @unit Staking contract', function () { await Staking.connect(accounts[1])['addStake(address,uint72,uint96)']( accounts[0].address, identityId1, - hre.ethers.utils.parseEther(`${5_000_000}`), + hre.ethers.utils.parseEther(`${2_000_000}`), ); expect(await StakingStorage.totalStakes(identityId1)).to.equal( - hre.ethers.utils.parseEther(`${5_000_000}`), + hre.ethers.utils.parseEther(`${2_000_000}`), 'Total amount of stake is not set', ); }); it('Add reward; expect that total stake is increased', async () => { - await Token.mint(ServiceAgreementStorageV1U1.address, hre.ethers.utils.parseEther(`${5_000_000}`)); + await Token.mint(ServiceAgreementStorageV1U1.address, hre.ethers.utils.parseEther(`${2_000_000}`)); const nodeId1 = '0x07f38512786964d9e70453371e7c98975d284100d44bd68dab67fe00b525cb66'; await Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN'); @@ -179,9 +179,9 @@ describe('@v1 @unit Staking contract', function () { proofWindowOffsetPerc, ); - await Staking.connect(accounts[1]).addReward(agreementId, identityId1, hre.ethers.utils.parseEther(`${5_000_000}`)); + await Staking.connect(accounts[1]).addReward(agreementId, identityId1, hre.ethers.utils.parseEther(`${2_000_000}`)); expect(await StakingStorage.totalStakes(identityId1)).to.equal( - hre.ethers.utils.parseEther(`${5_000_000}`), + hre.ethers.utils.parseEther(`${2_000_000}`), 'Total amount of stake is not increased after adding reward', ); }); diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts new file mode 100644 index 00000000..9239bc9f --- /dev/null +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -0,0 +1,519 @@ +import { randomBytes } from 'crypto'; + +import { loadFixture, time } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { expect } from 'chai'; +import { BytesLike, BigNumber } from 'ethers'; +import hre from 'hardhat'; + +import { + CommitManagerV2, + ContentAsset, + ContentAssetStorageV2, + LinearSum, + ParametersStorage, + Profile, + ProfileStorage, + ServiceAgreementV1, + StakingV2, + Token, +} from '../../../typechain'; +import { ContentAssetStructs } from '../../../typechain/contracts/v1/assets/ContentAsset'; +import { ServiceAgreementStructsV1 } from '../../../typechain/contracts/v1/CommitManagerV1'; +import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV2'; + +type CommitManagerV2Fixture = { + accounts: SignerWithAddress[]; + CommitManagerV2: CommitManagerV2; +}; + +type Node = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; + stake: BigNumber; +}; + +type NodeWithDistance = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; + stake: BigNumber; + index: BigNumber; + distance: BigNumber; +}; + +describe('@v2 @unit CommitManagerV2 contract', function () { + const HASH_RING_SIZE = BigNumber.from(2).pow(256).sub(1); + const UINT256_MAX_BN = BigNumber.from(2).pow(256).sub(1); + const UINT64_MAX_BN = BigNumber.from(2).pow(64).sub(1); + const UINT40_MAX_BN = BigNumber.from(2).pow(40).sub(1); + + let accounts: SignerWithAddress[]; + let Token: Token; + let ServiceAgreementV1: ServiceAgreementV1; + let ContentAsset: ContentAsset; + let ContentAssetStorageV2: ContentAssetStorageV2; + let LinearSum: LinearSum; + let CommitManagerV2: CommitManagerV2; + let ParametersStorage: ParametersStorage; + let ProfileStorage: ProfileStorage; + let Profile: Profile; + let StakingV2: StakingV2; + + let commitV1InputArgs: ServiceAgreementStructsV1.CommitInputArgsStruct; + let commitV2InputArgs: ServiceAgreementStructsV2.CommitInputArgsStruct; + + async function createAsset( + scoreFunctionId = 1, + ): Promise<{ tokenId: number; keyword: BytesLike; agreementId: BytesLike }> { + const assetInputStruct: ContentAssetStructs.AssetInputArgsStruct = { + assertionId: '0x' + randomBytes(32).toString('hex'), + size: 1000, + triplesNumber: 10, + chunksNumber: 10, + epochsNumber: 5, + tokenAmount: hre.ethers.utils.parseEther('250'), + scoreFunctionId, + immutable_: false, + }; + + await Token.increaseAllowance(ServiceAgreementV1.address, assetInputStruct.tokenAmount); + const receipt = await (await ContentAsset.createAsset(assetInputStruct)).wait(); + + const tokenId = Number(receipt.logs[0].topics[3]); + const keyword = hre.ethers.utils.solidityPack( + ['address', 'bytes32'], + [ContentAssetStorageV2.address, assetInputStruct.assertionId], + ); + const agreementId = hre.ethers.utils.soliditySha256( + ['address', 'uint256', 'bytes'], + [ContentAssetStorageV2.address, tokenId, keyword], + ); + + return { tokenId, keyword, agreementId }; + } + + async function createProfile(operational: SignerWithAddress, admin: SignerWithAddress): Promise { + const OperationalProfile = Profile.connect(operational); + + const nodeId = '0x' + randomBytes(32).toString('hex'); + const sha256 = hre.ethers.utils.soliditySha256(['bytes'], [nodeId]); + + const receipt = await ( + await OperationalProfile.createProfile( + admin.address, + nodeId, + randomBytes(5).toString('hex'), + randomBytes(3).toString('hex'), + ) + ).wait(); + const identityId = Number(receipt.logs[0].topics[1]); + const blockchainNodeId = await ProfileStorage.getNodeId(identityId); + const blockchainSha256 = await ProfileStorage.getNodeAddress(identityId, 1); + + expect(blockchainNodeId).to.be.equal(nodeId); + expect(blockchainSha256).to.be.equal(sha256); + + await OperationalProfile.setAsk(identityId, hre.ethers.utils.parseEther('0.25')); + + const minStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.minimumStake())); + const maxStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.maximumStake())); + const stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - minStake + 1)) + minStake}`, + ); + await Token.connect(admin).increaseAllowance(StakingV2.address, stakeAmount); + await StakingV2.connect(admin)['addStake(uint72,uint96)'](identityId, stakeAmount); + + return { + account: operational, + identityId, + nodeId, + sha256, + stake: stakeAmount, + }; + } + + async function createMultipleProfiles(count = 150): Promise { + const nodes = []; + + for (let i = 0; i < count; i++) { + const node = await createProfile(accounts[i], accounts[i + count]); + nodes.push(node); + } + + return nodes; + } + + function calculateDistance(peerHash: BytesLike, keyHash: BytesLike): BigNumber { + const peerPositionOnHashRing = BigNumber.from(peerHash); + const keyPositionOnHashRing = BigNumber.from(keyHash); + + const directDistance = peerPositionOnHashRing.gt(keyPositionOnHashRing) + ? peerPositionOnHashRing.sub(keyPositionOnHashRing) + : keyPositionOnHashRing.sub(peerPositionOnHashRing); + const wraparoundDistance = HASH_RING_SIZE.sub(directDistance); + + return directDistance.lt(wraparoundDistance) ? directDistance : wraparoundDistance; + } + + function toUint40(value: BigNumber, maxValue: BigNumber): BigNumber { + const result = value.mul(UINT40_MAX_BN).div(maxValue); + return result; + } + + async function calculateScore( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + minStake: BigNumber, + maxStake: BigNumber, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [distanceScaleFactor, stakeScaleFactor, w1, w2] = linearSumParams; + + const idealMaxDistanceInNeighborhood = HASH_RING_SIZE.div(nodesNumber).mul(Math.ceil(r2 / 2)); + const divisor = + maxNeighborhoodDistance <= idealMaxDistanceInNeighborhood + ? maxNeighborhoodDistance + : idealMaxDistanceInNeighborhood; + + const maxMultiplier = UINT256_MAX_BN.div(distance); + + let scaledDistanceScaleFactor = distanceScaleFactor; + let compensationFactor = BigNumber.from(1); + + if (scaledDistanceScaleFactor.gt(maxMultiplier)) { + compensationFactor = scaledDistanceScaleFactor.div(maxMultiplier); + scaledDistanceScaleFactor = maxMultiplier; + } + + const scaledDistance = distance.mul(scaledDistanceScaleFactor); + const adjustedDivisor = divisor.div(compensationFactor); + + let normalizedDistance = scaledDistance.div(adjustedDivisor); + if (normalizedDistance.gt(UINT64_MAX_BN)) { + normalizedDistance = normalizedDistance.mod(UINT64_MAX_BN.add(1)); + } + + let normalizedStake = stakeScaleFactor.mul(stake.sub(minStake)).div(maxStake.sub(minStake)); + if (normalizedStake.gt(UINT64_MAX_BN)) { + normalizedStake = normalizedStake.mod(UINT64_MAX_BN.add(1)); + } + + const oneEther = BigNumber.from('1000000000000000000'); + + const isProximityScorePositive = oneEther.gte(normalizedDistance); + + const proximityScore = isProximityScorePositive + ? oneEther.sub(normalizedDistance).mul(w1) + : normalizedDistance.sub(oneEther).mul(w1); + const stakeScore = normalizedStake.mul(w2); + + let finalScore; + if (isProximityScorePositive) { + finalScore = proximityScore.add(stakeScore); + } else if (stakeScore.gte(proximityScore)) { + finalScore = stakeScore.sub(proximityScore); + } else { + finalScore = BigNumber.from(0); + } + + finalScore = toUint40(finalScore, oneEther.mul(w1 + w2)); + + return finalScore; + } + + async function getNeighborhood(nodes: Node[], keyHash: BytesLike): Promise { + const nodesWithIndexes = nodes + .sort((a, b) => { + const aBN = BigNumber.from(a.sha256); + const bBN = BigNumber.from(b.sha256); + if (aBN.eq(bBN)) { + return 0; + } + return aBN.lt(bBN) ? -1 : 1; + }) + .map((node, index) => ({ ...node, index: BigNumber.from(index) })); + + const nodesWithDistance = await Promise.all( + nodesWithIndexes.map(async (node) => ({ + node, + distance: calculateDistance(node.sha256, keyHash), + })), + ); + nodesWithDistance.sort((a, b) => { + if (a.distance.eq(b.distance)) { + return 0; + } + return a.distance.lt(b.distance) ? -1 : 1; + }); + return nodesWithDistance.slice(0, 20).map((pd) => ({ ...pd.node, distance: pd.distance })); + } + + async function getNeighborhoodEdgeNodes( + neighborhood: NodeWithDistance[], + keyHash: BytesLike, + ): Promise<{ leftEdgeNode: NodeWithDistance; rightEdgeNode: NodeWithDistance }> { + const assetPositionOnHashRing = BigNumber.from(keyHash); + const hashRing = []; + + const maxDistance = neighborhood[neighborhood.length - 1].distance; + + for (const neighbor of neighborhood) { + const neighborPositionOnHashRing = BigNumber.from(neighbor.sha256); + + if (neighborPositionOnHashRing.lte(assetPositionOnHashRing)) { + if (assetPositionOnHashRing.sub(neighborPositionOnHashRing).lte(maxDistance)) { + hashRing.unshift(neighbor); + } else { + hashRing.push(neighbor); + } + } else { + if (neighborPositionOnHashRing.sub(assetPositionOnHashRing).lte(maxDistance)) { + hashRing.push(neighbor); + } else { + hashRing.unshift(neighbor); + } + } + } + + return { + leftEdgeNode: hashRing[0], + rightEdgeNode: hashRing[hashRing.length - 1], + }; + } + + async function deployCommitManagerV2Fixture(): Promise { + await hre.deployments.fixture([ + 'HubV2', + 'ContentAssetStorageV2', + 'ShardingTableV2', + 'StakingV2', + 'CommitManagerV2', + 'CommitManagerV2U1', + 'ContentAsset', + 'Profile', + ]); + Token = await hre.ethers.getContract('Token'); + ServiceAgreementV1 = await hre.ethers.getContract('ServiceAgreementV1'); + ContentAsset = await hre.ethers.getContract('ContentAsset'); + ContentAssetStorageV2 = await hre.ethers.getContract('ContentAssetStorage'); + LinearSum = await hre.ethers.getContract('LinearSum'); + CommitManagerV2 = await hre.ethers.getContract('CommitManagerV1'); + ParametersStorage = await hre.ethers.getContract('ParametersStorage'); + ProfileStorage = await hre.ethers.getContract('ProfileStorage'); + Profile = await hre.ethers.getContract('Profile'); + StakingV2 = await hre.ethers.getContract('Staking'); + accounts = await hre.ethers.getSigners(); + + return { accounts, CommitManagerV2 }; + } + + beforeEach(async () => { + hre.helpers.resetDeploymentsJson(); + ({ accounts, CommitManagerV2 } = await loadFixture(deployCommitManagerV2Fixture)); + }); + + it('The contract is named "CommitManagerV1"', async () => { + expect(await CommitManagerV2.name()).to.equal('CommitManagerV1'); + }); + + it('The contract is version "2.0.0"', async () => { + expect(await CommitManagerV2.version()).to.equal('2.0.0'); + }); + + it('Create new asset, check if commit window is open, expect to be true', async () => { + const { agreementId } = await createAsset(); + + expect(await CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.eql(true); + }); + + it('Create new asset, teleport to the end of commit phase and check if commit window is open, expect to be false', async () => { + const { agreementId } = await createAsset(); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + const commitWindowDurationPerc = await ParametersStorage.commitWindowDurationPerc(); + const commitWindowDuration = (epochLength * commitWindowDurationPerc) / 100; + + await time.increase(commitWindowDuration + 1); + + expect(await CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.eql(false); + }); + + it('Create new asset, teleport to second epoch and check if commit window is open, expect to be true', async () => { + const { agreementId } = await createAsset(); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + expect(await CommitManagerV2.isCommitWindowOpen(agreementId, 1)).to.eql(true); + }); + + it('Create new asset with scoreFunction 1, submit commit V1, expect CommitSubmitted event', async () => { + await createProfile(accounts[0], accounts[1]); + + const { tokenId, keyword } = await createAsset(); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + }; + + await expect(CommitManagerV2['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs)).to.emit( + CommitManagerV2, + 'CommitSubmitted', + ); + }); + + it('Create new asset with scoreFunction 2, submit commit V2, expect CommitSubmitted event', async () => { + const nodes = await createMultipleProfiles(30); + + const { tokenId, keyword } = await createAsset(2); + + const keyHash = hre.ethers.utils.soliditySha256(['bytes'], [keyword]); + + const neighborhood = await getNeighborhood(nodes, keyHash); + + const closestNode = neighborhood[0]; + const { leftEdgeNode, rightEdgeNode } = await getNeighborhoodEdgeNodes(neighborhood, keyHash); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + + const r2 = await ParametersStorage.r2(); + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + const score = await calculateScore( + closestNode.distance, + closestNode.stake, + neighborhood[neighborhood.length - 1].distance, + r2, + nodes.length, + minStake, + maxStake, + ); + + await expect( + CommitManagerV2.connect(closestNode.account)[ + 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ) + .to.emit(CommitManagerV2, 'CommitSubmitted') + .withArgs(ContentAssetStorageV2.address, tokenId, keyword, 1, 0, closestNode.identityId, score); + }); + + it('Create new asset with scoreFunction 1, submit R0 V1 commits, expect R0 V1 commits to be returned', async () => { + const r0 = await ParametersStorage.r0(); + + const identityIds = []; + for (let i = 0; i < r0; i++) { + const { identityId } = await createProfile(accounts[i], accounts[accounts.length - 1]); + identityIds.push(identityId); + } + + const { tokenId, keyword, agreementId } = await createAsset(); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + }; + + for (let i = 0; i < r0; i++) { + await expect( + CommitManagerV2.connect(accounts[i])['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs), + ).to.emit(CommitManagerV2, 'CommitSubmitted'); + } + + const topCommits = await CommitManagerV2.getTopCommitSubmissions(agreementId, 0); + + expect(topCommits.map((arr) => arr[0])).to.have.deep.members( + identityIds.map((identityId) => hre.ethers.BigNumber.from(identityId)), + ); + }); + + it('Create new asset with scoreFunction 2, submit R0 V2 commits, expect R0 V2 commits to be returned', async () => { + const r0 = await ParametersStorage.r0(); + const r2 = await ParametersStorage.r2(); + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + const nodes = await createMultipleProfiles(30); + + const { tokenId, keyword, agreementId } = await createAsset(2); + + const keyHash = hre.ethers.utils.soliditySha256(['bytes'], [keyword]); + + const neighborhood = await getNeighborhood(nodes, keyHash); + + const closestNode = neighborhood[0]; + const { leftEdgeNode, rightEdgeNode } = await getNeighborhoodEdgeNodes(neighborhood, keyHash); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + + const scoredNeighborhood = await Promise.all( + neighborhood.map(async (node) => ({ + account: node.account, + identityId: node.identityId, + score: ( + await calculateScore( + node.distance, + node.stake, + neighborhood[neighborhood.length - 1].distance, + r2, + nodes.length, + minStake, + maxStake, + ) + ).toNumber(), + })), + ); + + scoredNeighborhood.sort((a, b) => b.score - a.score); + + for (const node of [...scoredNeighborhood].reverse()) { + await expect( + CommitManagerV2.connect(node.account)[ + 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ) + .to.emit(CommitManagerV2, 'CommitSubmitted') + .withArgs(ContentAssetStorageV2.address, tokenId, keyword, 1, 0, node.identityId, node.score); + } + + const topCommits = await CommitManagerV2.getTopCommitSubmissions(agreementId, 0); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const expectedWinners = scoredNeighborhood.map(({ account, ...rest }) => rest).slice(0, r0); + + expect( + topCommits.map((commit) => ({ identityId: commit.identityId.toNumber(), score: commit.score })), + ).to.have.deep.members(expectedWinners); + }); +}); diff --git a/test/v2/unit/CommitManagerV2U1.test.ts b/test/v2/unit/CommitManagerV2U1.test.ts new file mode 100644 index 00000000..c1b9b860 --- /dev/null +++ b/test/v2/unit/CommitManagerV2U1.test.ts @@ -0,0 +1,874 @@ +import { randomBytes } from 'crypto'; + +import { loadFixture, time } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { expect } from 'chai'; +import { BytesLike, BigNumber } from 'ethers'; +import hre from 'hardhat'; + +import { + CommitManagerV2, + CommitManagerV2U1, + ContentAsset, + ContentAssetStorageV2, + LinearSum, + ParametersStorage, + Profile, + ProfileStorage, + ServiceAgreementV1, + StakingV2, + Token, +} from '../../../typechain'; +import { ContentAssetStructs } from '../../../typechain/contracts/v1/assets/ContentAsset'; +import { ServiceAgreementStructsV1 } from '../../../typechain/contracts/v1/CommitManagerV1U1'; +import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV2U1'; + +const UINT256_MAX_BN = BigNumber.from(2).pow(256).sub(1); +const UINT64_MAX_BN = BigNumber.from(2).pow(64).sub(1); +const UINT40_MAX_BN = BigNumber.from(2).pow(40).sub(1); + +type CommitManagerV2U1Fixture = { + accounts: SignerWithAddress[]; + CommitManagerV2: CommitManagerV2; + CommitManagerV2U1: CommitManagerV2U1; +}; + +type Node = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; + stake: BigNumber; +}; + +type NodeWithDistance = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; + stake: BigNumber; + index: BigNumber; + distance: BigNumber; +}; + +describe('@v2 @unit CommitManagerV2U1 contract', function () { + const HASH_RING_SIZE = BigNumber.from(2).pow(256); + + let accounts: SignerWithAddress[]; + let Token: Token; + let ServiceAgreementV1: ServiceAgreementV1; + let ContentAsset: ContentAsset; + let ContentAssetStorageV2: ContentAssetStorageV2; + let LinearSum: LinearSum; + let CommitManagerV2: CommitManagerV2; + let CommitManagerV2U1: CommitManagerV2U1; + let ParametersStorage: ParametersStorage; + let ProfileStorage: ProfileStorage; + let Profile: Profile; + let StakingV2: StakingV2; + + let commitV1InputArgs: ServiceAgreementStructsV1.CommitInputArgsStruct; + let commitV2InputArgs: ServiceAgreementStructsV2.CommitInputArgsStruct; + + async function createAsset( + scoreFunctionId = 1, + ): Promise<{ tokenId: number; keyword: BytesLike; agreementId: BytesLike }> { + const assetInputStruct: ContentAssetStructs.AssetInputArgsStruct = { + assertionId: '0x' + randomBytes(32).toString('hex'), + size: 1000, + triplesNumber: 10, + chunksNumber: 10, + epochsNumber: 5, + tokenAmount: hre.ethers.utils.parseEther('250'), + scoreFunctionId, + immutable_: false, + }; + + await Token.increaseAllowance(ServiceAgreementV1.address, assetInputStruct.tokenAmount); + const receipt = await (await ContentAsset.createAsset(assetInputStruct)).wait(); + + const tokenId = Number(receipt.logs[0].topics[3]); + const keyword = hre.ethers.utils.solidityPack( + ['address', 'bytes32'], + [ContentAssetStorageV2.address, assetInputStruct.assertionId], + ); + const agreementId = hre.ethers.utils.soliditySha256( + ['address', 'uint256', 'bytes'], + [ContentAssetStorageV2.address, tokenId, keyword], + ); + + return { tokenId, keyword, agreementId }; + } + + async function updateAsset(tokenId: number) { + const assetUpdateArgs = { + assertionId: '0x' + randomBytes(32).toString('hex'), + size: 2000, + triplesNumber: 20, + chunksNumber: 20, + tokenAmount: hre.ethers.utils.parseEther('500'), + }; + + await Token.increaseAllowance(ServiceAgreementV1.address, assetUpdateArgs.tokenAmount); + await ContentAsset.updateAssetState( + tokenId, + assetUpdateArgs.assertionId, + assetUpdateArgs.size, + assetUpdateArgs.triplesNumber, + assetUpdateArgs.chunksNumber, + assetUpdateArgs.tokenAmount, + ); + } + + async function finalizeUpdateV1(tokenId: number, keyword: BytesLike): Promise { + const finalizationRequirement = await ParametersStorage.finalizationCommitsNumber(); + + const nodes = []; + for (let i = 0; i < finalizationRequirement; i++) { + nodes.push(await createProfile(accounts[i], accounts[accounts.length - 1])); + } + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + }; + + for (let i = 0; i < finalizationRequirement; i++) { + await expect( + CommitManagerV2U1.connect(accounts[i])['submitUpdateCommit((address,uint256,bytes,uint8,uint16))']( + commitV1InputArgs, + ), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + + return nodes; + } + + async function finalizeUpdateV2( + tokenId: number, + keyword: BytesLike, + ): Promise<{ + winners: { account: SignerWithAddress; identityId: number; score: number }[]; + closestNodeIndex: BigNumber; + leftEdgeNodeIndex: BigNumber; + rightEdgeNodeIndex: BigNumber; + }> { + const finalizationRequirement = await ParametersStorage.finalizationCommitsNumber(); + + const r2 = await ParametersStorage.r2(); + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + const nodes = await createMultipleProfiles(30); + + const keyHash = hre.ethers.utils.soliditySha256(['bytes'], [keyword]); + + const neighborhood = await getNeighborhood(nodes, keyHash); + + const closestNode = neighborhood[0]; + const { leftEdgeNode, rightEdgeNode } = await getNeighborhoodEdgeNodes(neighborhood, keyHash); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + + const scoredNeighborhood = await Promise.all( + neighborhood.map(async (node) => ({ + account: node.account, + identityId: node.identityId, + score: ( + await calculateScore( + node.distance, + node.stake, + neighborhood[neighborhood.length - 1].distance, + r2, + nodes.length, + minStake, + maxStake, + ) + ).toNumber(), + })), + ); + + scoredNeighborhood.sort((a, b) => a.score - b.score); + + for (let i = 0; i < finalizationRequirement; i++) { + await expect( + CommitManagerV2U1.connect(scoredNeighborhood[i].account)[ + 'submitUpdateCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + + return { + winners: scoredNeighborhood.slice(0, finalizationRequirement), + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + } + + async function createProfile(operational: SignerWithAddress, admin: SignerWithAddress): Promise { + const { minter } = await hre.getNamedAccounts(); + const OperationalProfile = Profile.connect(operational); + + const nodeId = '0x' + randomBytes(32).toString('hex'); + const sha256 = hre.ethers.utils.soliditySha256(['bytes'], [nodeId]); + + const receipt = await ( + await OperationalProfile.createProfile( + admin.address, + nodeId, + randomBytes(5).toString('hex'), + randomBytes(3).toString('hex'), + ) + ).wait(); + const identityId = Number(receipt.logs[0].topics[1]); + const blockchainNodeId = await ProfileStorage.getNodeId(identityId); + const blockchainSha256 = await ProfileStorage.getNodeAddress(identityId, 1); + + expect(blockchainNodeId).to.be.equal(nodeId); + expect(blockchainSha256).to.be.equal(sha256); + + await OperationalProfile.setAsk(identityId, hre.ethers.utils.parseEther('0.25')); + + const minStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.minimumStake())); + const maxStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.maximumStake())); + const stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - minStake + 1)) + minStake}`, + ); + await Token.mint(admin.address, stakeAmount, { from: minter }); + await Token.connect(admin).increaseAllowance(StakingV2.address, stakeAmount); + await StakingV2.connect(admin)['addStake(uint72,uint96)'](identityId, stakeAmount); + + return { + account: operational, + identityId, + nodeId, + sha256, + stake: stakeAmount, + }; + } + + async function createMultipleProfiles(count = 150): Promise { + const nodes = []; + + for (let i = 0; i < count; i++) { + const node = await createProfile(accounts[i], accounts[i + count]); + nodes.push(node); + } + + return nodes; + } + + function calculateDistance(peerHash: BytesLike, keyHash: BytesLike): BigNumber { + const peerPositionOnHashRing = BigNumber.from(peerHash); + const keyPositionOnHashRing = BigNumber.from(keyHash); + + const directDistance = peerPositionOnHashRing.gt(keyPositionOnHashRing) + ? peerPositionOnHashRing.sub(keyPositionOnHashRing) + : keyPositionOnHashRing.sub(peerPositionOnHashRing); + const wraparoundDistance = HASH_RING_SIZE.sub(directDistance); + + return directDistance.lt(wraparoundDistance) ? directDistance : wraparoundDistance; + } + + function toUint40(value: BigNumber, maxValue: BigNumber): BigNumber { + const result = value.mul(UINT40_MAX_BN).div(maxValue); + return result; + } + + async function calculateScore( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + minStake: BigNumber, + maxStake: BigNumber, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [distanceScaleFactor, stakeScaleFactor, w1, w2] = linearSumParams; + + const idealMaxDistanceInNeighborhood = HASH_RING_SIZE.div(nodesNumber).mul(Math.ceil(r2 / 2)); + const divisor = + maxNeighborhoodDistance <= idealMaxDistanceInNeighborhood + ? maxNeighborhoodDistance + : idealMaxDistanceInNeighborhood; + + const maxMultiplier = UINT256_MAX_BN.div(distance); + + let scaledDistanceScaleFactor = distanceScaleFactor; + let compensationFactor = BigNumber.from(1); + + if (scaledDistanceScaleFactor.gt(maxMultiplier)) { + compensationFactor = scaledDistanceScaleFactor.div(maxMultiplier); + scaledDistanceScaleFactor = maxMultiplier; + } + + const scaledDistance = distance.mul(scaledDistanceScaleFactor); + const adjustedDivisor = divisor.div(compensationFactor); + + let normalizedDistance = scaledDistance.div(adjustedDivisor); + if (normalizedDistance.gt(UINT64_MAX_BN)) { + normalizedDistance = normalizedDistance.mod(UINT64_MAX_BN.add(1)); + } + + let normalizedStake = stakeScaleFactor.mul(stake.sub(minStake)).div(maxStake.sub(minStake)); + if (normalizedStake.gt(UINT64_MAX_BN)) { + normalizedStake = normalizedStake.mod(UINT64_MAX_BN.add(1)); + } + + const oneEther = BigNumber.from('1000000000000000000'); + + const isProximityScorePositive = oneEther.gte(normalizedDistance); + + const proximityScore = isProximityScorePositive + ? oneEther.sub(normalizedDistance).mul(w1) + : normalizedDistance.sub(oneEther).mul(w1); + const stakeScore = normalizedStake.mul(w2); + + let finalScore; + if (isProximityScorePositive) { + finalScore = proximityScore.add(stakeScore); + } else if (stakeScore.gte(proximityScore)) { + finalScore = stakeScore.sub(proximityScore); + } else { + finalScore = BigNumber.from(0); + } + + finalScore = toUint40(finalScore, oneEther.mul(w1 + w2)); + + return finalScore; + } + + async function getNeighborhood(nodes: Node[], keyHash: BytesLike): Promise { + const nodesWithIndexes = nodes + .sort((a, b) => { + const aBN = BigNumber.from(a.sha256); + const bBN = BigNumber.from(b.sha256); + if (aBN.eq(bBN)) { + return 0; + } + return aBN.lt(bBN) ? -1 : 1; + }) + .map((node, index) => ({ ...node, index: BigNumber.from(index) })); + + const nodesWithDistance = await Promise.all( + nodesWithIndexes.map(async (node) => ({ + node, + distance: calculateDistance(node.sha256, keyHash), + })), + ); + nodesWithDistance.sort((a, b) => { + if (a.distance.eq(b.distance)) { + return 0; + } + return a.distance.lt(b.distance) ? -1 : 1; + }); + return nodesWithDistance.slice(0, 20).map((pd) => ({ ...pd.node, distance: pd.distance })); + } + + async function getNeighborhoodEdgeNodes( + neighborhood: NodeWithDistance[], + keyHash: BytesLike, + ): Promise<{ leftEdgeNode: NodeWithDistance; rightEdgeNode: NodeWithDistance }> { + const assetPositionOnHashRing = BigNumber.from(keyHash); + const hashRing = []; + + const maxDistance = neighborhood[neighborhood.length - 1].distance; + + for (const neighbor of neighborhood) { + const neighborPositionOnHashRing = BigNumber.from(neighbor.sha256); + + if (neighborPositionOnHashRing.lte(assetPositionOnHashRing)) { + if (assetPositionOnHashRing.sub(neighborPositionOnHashRing).lte(maxDistance)) { + hashRing.unshift(neighbor); + } else { + hashRing.push(neighbor); + } + } else { + if (neighborPositionOnHashRing.sub(assetPositionOnHashRing).lte(maxDistance)) { + hashRing.push(neighbor); + } else { + hashRing.unshift(neighbor); + } + } + } + + return { + leftEdgeNode: hashRing[0], + rightEdgeNode: hashRing[hashRing.length - 1], + }; + } + + async function deployCommitManagerV2U1Fixture(): Promise { + await hre.deployments.fixture([ + 'HubV2', + 'ContentAssetStorageV2', + 'ShardingTableV2', + 'StakingV2', + 'CommitManagerV2', + 'CommitManagerV2U1', + 'ContentAsset', + 'Profile', + ]); + Token = await hre.ethers.getContract('Token'); + ServiceAgreementV1 = await hre.ethers.getContract('ServiceAgreementV1'); + ContentAsset = await hre.ethers.getContract('ContentAsset'); + ContentAssetStorageV2 = await hre.ethers.getContract('ContentAssetStorage'); + LinearSum = await hre.ethers.getContract('LinearSum'); + CommitManagerV2 = await hre.ethers.getContract('CommitManagerV1'); + CommitManagerV2U1 = await hre.ethers.getContract('CommitManagerV1U1'); + ParametersStorage = await hre.ethers.getContract('ParametersStorage'); + ProfileStorage = await hre.ethers.getContract('ProfileStorage'); + Profile = await hre.ethers.getContract('Profile'); + StakingV2 = await hre.ethers.getContract('Staking'); + ContentAssetStorageV2 = await hre.ethers.getContract('ContentAssetStorage'); + accounts = await hre.ethers.getSigners(); + + return { accounts, CommitManagerV2, CommitManagerV2U1 }; + } + + beforeEach(async () => { + hre.helpers.resetDeploymentsJson(); + ({ accounts, CommitManagerV2U1 } = await loadFixture(deployCommitManagerV2U1Fixture)); + }); + + it('The contract is named "CommitManagerV1U1"', async () => { + expect(await CommitManagerV2U1.name()).to.equal('CommitManagerV1U1'); + }); + + it('The contract is version "2.0.0"', async () => { + expect(await CommitManagerV2U1.version()).to.equal('2.0.0'); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update and finalize V1 update, check if commit window is open, expect to be true', async () => { + const { tokenId, keyword, agreementId } = await createAsset(); + await updateAsset(tokenId); + await finalizeUpdateV1(tokenId, keyword); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 0)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update and finalize V2 update, check if commit window is open, expect to be true', async () => { + const { tokenId, keyword, agreementId } = await createAsset(2); + await updateAsset(tokenId); + await finalizeUpdateV2(tokenId, keyword); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 0)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update and finalize V1 update, teleport to the end of commit phase and check if commit window is open, expect to be false', async () => { + const { tokenId, keyword, agreementId } = await createAsset(); + await updateAsset(tokenId); + await finalizeUpdateV1(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + const commitWindowDurationPerc = await ParametersStorage.commitWindowDurationPerc(); + const commitWindowDuration = (epochLength * commitWindowDurationPerc) / 100; + + await time.increase(commitWindowDuration + 1); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 0)).to.eql(false); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update and finalize V2 update, teleport to the end of commit phase and check if commit window is open, expect to be false', async () => { + const { tokenId, keyword, agreementId } = await createAsset(2); + await updateAsset(tokenId); + await finalizeUpdateV2(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + const commitWindowDurationPerc = await ParametersStorage.commitWindowDurationPerc(); + const commitWindowDuration = (epochLength * commitWindowDurationPerc) / 100; + + await time.increase(commitWindowDuration + 1); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 0)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 0)).to.eql(false); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update and finalize update V1, teleport to second epoch and check if commit window is open, expect to be true', async () => { + const { tokenId, keyword, agreementId } = await createAsset(); + await updateAsset(tokenId); + await finalizeUpdateV1(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 1)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 1)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update and finalize update V2, teleport to second epoch and check if commit window is open, expect to be true', async () => { + const { tokenId, keyword, agreementId } = await createAsset(2); + await updateAsset(tokenId); + await finalizeUpdateV2(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + await expect(CommitManagerV2.isCommitWindowOpen(agreementId, 1)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + expect(await CommitManagerV2U1.isCommitWindowOpen(agreementId, 1)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update it, check if update commit window is open, expect to be true', async () => { + const { tokenId, agreementId } = await createAsset(); + await updateAsset(tokenId); + + expect(await CommitManagerV2U1.isUpdateCommitWindowOpen(agreementId, 0, 1)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update it, check if update commit window is open, expect to be true', async () => { + const { tokenId, agreementId } = await createAsset(2); + await updateAsset(tokenId); + + expect(await CommitManagerV2U1.isUpdateCommitWindowOpen(agreementId, 0, 1)).to.eql(true); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update it, teleport to the end of update commit window and check if its open, expect to be false', async () => { + const { tokenId, agreementId } = await createAsset(); + await updateAsset(tokenId); + + const updateCommitWindowDuration = await ParametersStorage.updateCommitWindowDuration(); + await time.increase(updateCommitWindowDuration); + + expect(await CommitManagerV2U1.isUpdateCommitWindowOpen(agreementId, 0, 1)).to.eql(false); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update it, teleport to the end of update commit window and check if its open, expect to be false', async () => { + const { tokenId, agreementId } = await createAsset(2); + await updateAsset(tokenId); + + const updateCommitWindowDuration = await ParametersStorage.updateCommitWindowDuration(); + await time.increase(updateCommitWindowDuration); + + expect(await CommitManagerV2U1.isUpdateCommitWindowOpen(agreementId, 0, 1)).to.eql(false); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update it, finalize V1 update, teleport to the second epoch, submit commit, expect CommitSubmitted event', async () => { + const { tokenId, keyword } = await createAsset(); + await updateAsset(tokenId); + await finalizeUpdateV1(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 1, + }; + + await expect( + CommitManagerV2['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV2InputArgs), + ).to.be.revertedWithCustomError(CommitManagerV2, 'ServiceAgreementDoesntExist'); + await expect(CommitManagerV2U1['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs)).to.emit( + CommitManagerV2U1, + 'CommitSubmitted', + ); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update it, finalize V2 update, teleport to the second epoch, submit commit, expect CommitSubmitted event', async () => { + const { tokenId, keyword } = await createAsset(2); + await updateAsset(tokenId); + const { closestNodeIndex, leftEdgeNodeIndex, rightEdgeNodeIndex } = await finalizeUpdateV2(tokenId, keyword); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 1, + closestNodeIndex: closestNodeIndex, + leftEdgeNodeIndex: leftEdgeNodeIndex, + rightEdgeNodeIndex: rightEdgeNodeIndex, + }; + + await expect( + CommitManagerV2['submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))'](commitV2InputArgs), + ).to.be.revertedWithCustomError(CommitManagerV2, 'ServiceAgreementDoesntExist'); + await expect( + CommitManagerV2U1['submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))'](commitV2InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update it, finalize update V1, teleport to the second epoch, submit R0 commits, expect R0 commits to be returned', async () => { + const r0 = await ParametersStorage.r0(); + + const { tokenId, keyword, agreementId } = await createAsset(); + await updateAsset(tokenId); + const nodes = await finalizeUpdateV1(tokenId, keyword); + const identityIds = nodes.map((node) => node.identityId); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 1, + }; + + for (let i = 0; i < r0; i++) { + await expect( + CommitManagerV2.connect(accounts[i])['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs), + ).to.be.revertedWithCustomError(CommitManagerV2, 'ServiceAgreementDoesntExist'); + await expect( + CommitManagerV2U1.connect(accounts[i])['submitCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + + await expect(CommitManagerV2.getTopCommitSubmissions(agreementId, 1)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + const topCommits = await CommitManagerV2U1.getTopCommitSubmissions(agreementId, 1, 1); + + expect(topCommits.map((arr) => arr[0])).to.have.deep.members( + identityIds.map((identityId) => hre.ethers.BigNumber.from(identityId)), + ); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update it, finalize update V2, teleport to the second epoch, submit R0 commits, expect R0 commits to be returned', async () => { + const r0 = await ParametersStorage.r0(); + + const { tokenId, keyword, agreementId } = await createAsset(2); + await updateAsset(tokenId); + const { winners, closestNodeIndex, leftEdgeNodeIndex, rightEdgeNodeIndex } = await finalizeUpdateV2( + tokenId, + keyword, + ); + + const epochLength = (await ParametersStorage.epochLength()).toNumber(); + await time.increase(epochLength); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 1, + closestNodeIndex: closestNodeIndex, + leftEdgeNodeIndex: leftEdgeNodeIndex, + rightEdgeNodeIndex: rightEdgeNodeIndex, + }; + + for (let i = 0; i < r0; i++) { + await expect( + CommitManagerV2.connect(winners[i].account)[ + 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.be.revertedWithCustomError(CommitManagerV2, 'ServiceAgreementDoesntExist'); + await expect( + CommitManagerV2U1.connect(winners[i].account)[ + 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + + await expect(CommitManagerV2.getTopCommitSubmissions(agreementId, 1)).to.be.revertedWithCustomError( + CommitManagerV2, + 'ServiceAgreementDoesntExist', + ); + const topCommits = await CommitManagerV2U1.getTopCommitSubmissions(agreementId, 1, 1); + + expect(topCommits.map((arr) => arr[0])).to.have.deep.members( + winners.map((winner) => hre.ethers.BigNumber.from(winner.identityId)), + ); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update asset, submit update commit V1, expect CommitSubmitted event', async () => { + await createProfile(accounts[0], accounts[1]); + + const { tokenId, keyword } = await createAsset(); + await updateAsset(tokenId); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + }; + + await expect( + CommitManagerV2U1['submitUpdateCommit((address,uint256,bytes,uint8,uint16))'](commitV1InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update asset, submit update commit V2, expect CommitSubmitted event', async () => { + const { tokenId, keyword } = await createAsset(2); + + const nodes = await createMultipleProfiles(30); + + const keyHash = hre.ethers.utils.soliditySha256(['bytes'], [keyword]); + + const neighborhood = await getNeighborhood(nodes, keyHash); + + const closestNode = neighborhood[0]; + const { leftEdgeNode, rightEdgeNode } = await getNeighborhoodEdgeNodes(neighborhood, keyHash); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + + await updateAsset(tokenId); + + await expect( + CommitManagerV2U1['submitUpdateCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))']( + commitV2InputArgs, + ), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + }); + + it('Create new asset with proximityScoreFunctionsPair 1, update it and submit V1 update commits, expect StateFinalized event', async () => { + const finalizationRequirement = await ParametersStorage.finalizationCommitsNumber(); + + const identityIds = []; + for (let i = 0; i < finalizationRequirement; i++) { + const node = await createProfile(accounts[i], accounts[accounts.length - 1]); + identityIds.push(node.identityId); + } + + const { tokenId, keyword, agreementId } = await createAsset(); + await updateAsset(tokenId); + + commitV1InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + }; + for (let i = 0; i < finalizationRequirement - 1; i++) { + await expect( + CommitManagerV2U1.connect(accounts[i])['submitUpdateCommit((address,uint256,bytes,uint8,uint16))']( + commitV1InputArgs, + ), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + await expect( + CommitManagerV2U1.connect(accounts[identityIds.length - 1])[ + 'submitUpdateCommit((address,uint256,bytes,uint8,uint16))' + ](commitV1InputArgs), + ).to.emit(CommitManagerV2U1, 'StateFinalized'); + const topCommits = await CommitManagerV2U1.getTopCommitSubmissions(agreementId, 0, 1); + expect(topCommits.map((arr) => arr[0])).to.include.deep.members( + identityIds.map((identityId) => hre.ethers.BigNumber.from(identityId)), + ); + }); + + it('Create new asset with proximityScoreFunctionsPair 2, update it and submit V2 update commits, expect StateFinalized event', async () => { + const r2 = await ParametersStorage.r2(); + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + const finalizationRequirement = await ParametersStorage.finalizationCommitsNumber(); + + const { tokenId, keyword, agreementId } = await createAsset(2); + + const nodes = await createMultipleProfiles(30); + + const keyHash = hre.ethers.utils.soliditySha256(['bytes'], [keyword]); + + const neighborhood = await getNeighborhood(nodes, keyHash); + + const closestNode = neighborhood[0]; + const { leftEdgeNode, rightEdgeNode } = await getNeighborhoodEdgeNodes(neighborhood, keyHash); + + await updateAsset(tokenId); + + commitV2InputArgs = { + assetContract: ContentAssetStorageV2.address, + tokenId: tokenId, + keyword: keyword, + hashFunctionId: 1, + epoch: 0, + closestNodeIndex: closestNode.index, + leftEdgeNodeIndex: leftEdgeNode.index, + rightEdgeNodeIndex: rightEdgeNode.index, + }; + + const scoredNeighborhood = await Promise.all( + neighborhood.map(async (node) => ({ + account: node.account, + identityId: node.identityId, + score: ( + await calculateScore( + node.distance, + node.stake, + neighborhood[neighborhood.length - 1].distance, + r2, + nodes.length, + minStake, + maxStake, + ) + ).toNumber(), + })), + ); + + scoredNeighborhood.sort((a, b) => a.score - b.score); + + for (let i = 0; i < finalizationRequirement - 1; i++) { + await expect( + CommitManagerV2U1.connect(scoredNeighborhood[i].account)[ + 'submitUpdateCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.emit(CommitManagerV2U1, 'CommitSubmitted'); + } + await expect( + CommitManagerV2U1.connect(scoredNeighborhood[finalizationRequirement - 1].account)[ + 'submitUpdateCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.emit(CommitManagerV2U1, 'StateFinalized'); + const topCommits = await CommitManagerV2U1.getTopCommitSubmissions(agreementId, 0, 1); + expect(topCommits.map((arr) => arr[0])).to.include.deep.members( + scoredNeighborhood.slice(0, finalizationRequirement).map((node) => hre.ethers.BigNumber.from(node.identityId)), + ); + }); +}); diff --git a/test/v2/unit/ContentAssetStorageV2.test.ts b/test/v2/unit/ContentAssetStorageV2.test.ts index 92ea1b94..e18903b3 100644 --- a/test/v2/unit/ContentAssetStorageV2.test.ts +++ b/test/v2/unit/ContentAssetStorageV2.test.ts @@ -39,7 +39,19 @@ describe('@v2 @unit ContentAssetStorageV2', function () { }; async function deployContentAssetStorageV2Fixture(): Promise { - await hre.deployments.fixture(['HubV2', 'Token', 'ContentAssetStorageV2', 'IdentityStorageV2', 'ContentAsset']); + await hre.deployments.fixture([ + 'HubV2', + 'Token', + 'LinearSum', + 'ContentAssetStorageV2', + 'IdentityStorageV2', + 'ShardingTableStorageV2', + 'ShardingTableV2', + 'StakingV2', + 'CommitManagerV2', + 'CommitManagerV2U1', + 'ContentAsset', + ]); HubController = await hre.ethers.getContract('HubController'); Token = await hre.ethers.getContract('Token'); ServiceAgreementV1 = await hre.ethers.getContract('ServiceAgreementV1'); diff --git a/test/v2/unit/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts new file mode 100644 index 00000000..0f55a82c --- /dev/null +++ b/test/v2/unit/LinearSum.test.ts @@ -0,0 +1,329 @@ +import { randomBytes } from 'crypto'; + +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import { BigNumber, BytesLike } from 'ethers'; +import hre from 'hardhat'; + +import { LinearSum, ParametersStorage } from '../../../typechain'; +import { ZERO_BYTES32 } from '../../helpers/constants'; + +type LinearSumFixture = { + accounts: SignerWithAddress[]; + LinearSum: LinearSum; +}; + +describe('@v2 @unit LinearSum', function () { + const HASH_RING_SIZE = BigNumber.from(2).pow(256).sub(1); + const UINT256_MAX_BN = BigNumber.from(2).pow(256).sub(1); + const UINT64_MAX_BN = BigNumber.from(2).pow(64).sub(1); + const UINT40_MAX_BN = BigNumber.from(2).pow(40).sub(1); + + let accounts: SignerWithAddress[]; + let ParametersStorage: ParametersStorage; + let LinearSum: LinearSum; + + async function deployLinearSumFixture(): Promise { + await hre.deployments.fixture(['LinearSum']); + ParametersStorage = await hre.ethers.getContract('ParametersStorage'); + LinearSum = await hre.ethers.getContract('LinearSum'); + accounts = await hre.ethers.getSigners(); + + return { accounts, LinearSum }; + } + + function generateRandomHashes(count: number): string[] { + const hashes = []; + + for (let i = 0; i < count; i += 1) { + hashes.push('0x' + randomBytes(32).toString('hex')); + } + + return hashes; + } + + function calculateDistance(peerHash: BytesLike, keyHash: BytesLike): BigNumber { + const peerPositionOnHashRing = BigNumber.from(peerHash); + const keyPositionOnHashRing = BigNumber.from(keyHash); + + const directDistance = peerPositionOnHashRing.gt(keyPositionOnHashRing) + ? peerPositionOnHashRing.sub(keyPositionOnHashRing) + : keyPositionOnHashRing.sub(peerPositionOnHashRing); + const wraparoundDistance = HASH_RING_SIZE.sub(directDistance); + + return directDistance.lt(wraparoundDistance) ? directDistance : wraparoundDistance; + } + + function toUint40(value: BigNumber, maxValue: BigNumber): BigNumber { + const result = value.mul(UINT40_MAX_BN).div(maxValue); + return result; + } + + async function calculateNormalizedDistance( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [distanceScaleFactor] = linearSumParams; + + const idealMaxDistanceInNeighborhood = HASH_RING_SIZE.div(nodesNumber).mul(Math.ceil(r2 / 2)); + + const divisor = maxNeighborhoodDistance.lte(idealMaxDistanceInNeighborhood) + ? maxNeighborhoodDistance + : idealMaxDistanceInNeighborhood; + + const maxMultiplier = UINT256_MAX_BN.div(distance); + + let scaledDistanceScaleFactor = distanceScaleFactor; + let compensationFactor = BigNumber.from(1); + + if (scaledDistanceScaleFactor.gt(maxMultiplier)) { + compensationFactor = scaledDistanceScaleFactor.div(maxMultiplier); + scaledDistanceScaleFactor = maxMultiplier; + } + + const scaledDistance = distance.mul(scaledDistanceScaleFactor); + const adjustedDivisor = divisor.div(compensationFactor); + + let normalizedDistance = scaledDistance.div(adjustedDivisor); + if (normalizedDistance.gt(UINT64_MAX_BN)) { + normalizedDistance = normalizedDistance.mod(UINT64_MAX_BN.add(1)); + } + + return normalizedDistance; + } + + async function calculateProximityScore( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [, , w1] = linearSumParams; + + const normalizedDistance = await calculateNormalizedDistance( + distance, + stake, + maxNeighborhoodDistance, + r2, + nodesNumber, + ); + + const oneEther = BigNumber.from('1000000000000000000'); + + const isProximityScorePositive = oneEther.gte(normalizedDistance); + + const proximityScore = isProximityScorePositive + ? oneEther.sub(normalizedDistance).mul(w1) + : normalizedDistance.sub(oneEther).mul(w1); + + return proximityScore; + } + + async function calculateStakeScore( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + minStake: BigNumber, + maxStake: BigNumber, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [, stakeScaleFactor, , w2] = linearSumParams; + + let normalizedStake = stakeScaleFactor.mul(stake.sub(minStake)).div(maxStake.sub(minStake)); + if (normalizedStake.gt(UINT64_MAX_BN)) { + normalizedStake = normalizedStake.mod(UINT64_MAX_BN.add(1)); + } + const stakeScore = normalizedStake.mul(w2); + + return stakeScore; + } + + async function calculateScoreBeforeScaling( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + minStake: BigNumber, + maxStake: BigNumber, + ): Promise { + const proximityScore = await calculateProximityScore(distance, stake, maxNeighborhoodDistance, r2, nodesNumber); + const normalizedDistance = await calculateNormalizedDistance( + distance, + stake, + maxNeighborhoodDistance, + r2, + nodesNumber, + ); + const stakeScore = await calculateStakeScore( + distance, + stake, + maxNeighborhoodDistance, + r2, + nodesNumber, + minStake, + maxStake, + ); + + const oneEther = BigNumber.from('1000000000000000000'); + + const isProximityScorePositive = oneEther.gte(normalizedDistance); + let finalScore; + + if (isProximityScorePositive) { + finalScore = proximityScore.add(stakeScore); + } else if (stakeScore.gte(proximityScore)) { + finalScore = stakeScore.sub(proximityScore); + } else { + finalScore = BigNumber.from(0); + } + + return finalScore; + } + + async function calculateScore( + distance: BigNumber, + stake: BigNumber, + maxNeighborhoodDistance: BigNumber, + r2: number, + nodesNumber: number, + minStake: BigNumber, + maxStake: BigNumber, + ): Promise { + const linearSumParams = await LinearSum.getParameters(); + const [, , w1, w2] = linearSumParams; + const oneEther = BigNumber.from('1000000000000000000'); + const scoreWithoutScaling = await calculateScoreBeforeScaling( + distance, + stake, + maxNeighborhoodDistance, + r2, + nodesNumber, + minStake, + maxStake, + ); + const finalScore = toUint40(scoreWithoutScaling, oneEther.mul(w1 + w2)); + + return finalScore; + } + + beforeEach(async function () { + hre.helpers.resetDeploymentsJson(); + ({ accounts, LinearSum } = await loadFixture(deployLinearSumFixture)); + }); + + it('Should deploy successfully with correct initial parameters', async function () { + expect(await LinearSum.name()).to.equal('LinearSum'); + expect(await LinearSum.getParameters()).to.eql([ + BigNumber.from('1000000000000000000'), + BigNumber.from('1000000000000000000'), + 1, + 1, + ]); + }); + + it('Should calculate distance correctly for 30 random nodes/KAs', async function () { + const peerIds = generateRandomHashes(30); + const keywords = generateRandomHashes(30); + + const peerHashes = peerIds.map((peerId) => hre.ethers.utils.soliditySha256(['bytes'], [peerId])); + const keyHashes = keywords.map((keyword) => hre.ethers.utils.soliditySha256(['bytes'], [keyword])); + + for (let i = 0; i < peerHashes.length; i += 1) { + const expectedDistance = calculateDistance(peerHashes[i], keyHashes[i]); + const calculatedDistance = await LinearSum.calculateDistance(1, peerIds[i], keywords[i]); + + expect(calculatedDistance).to.be.equal(expectedDistance); + } + }); + + it('Should calculate score correctly for 30 random nodes/KAs', async function () { + const r2 = await ParametersStorage.r2(); + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + + const minStakeNumber = Number(hre.ethers.utils.formatEther(minStake)); + const maxStakeNumber = Number(hre.ethers.utils.formatEther(maxStake)); + + const peerHashes = generateRandomHashes(30); + const keyHashes = generateRandomHashes(30); + + for (const keyHash of keyHashes) { + const peerHashesWithDistances = peerHashes.map((peerHash) => ({ + hash: peerHash, + distance: calculateDistance(peerHash, keyHash), + })); + + const maxDistance = peerHashesWithDistances.reduce( + (max, obj) => + BigNumber.from(obj.hash).gt(BigNumber.from(max)) ? BigNumber.from(obj.hash) : BigNumber.from(max), + BigNumber.from(peerHashesWithDistances[0].hash), + ); + + for (const peerHash of peerHashesWithDistances) { + const stake = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStakeNumber - minStakeNumber + 1)) + minStakeNumber}`, + ); + const expectedScore = await calculateScore( + peerHash.distance, + stake, + maxDistance, + r2, + peerHashes.length, + minStake, + maxStake, + ); + const calculatedScore = await LinearSum.calculateScore( + peerHash.distance, + maxDistance, + peerHashes.length, + stake, + ); + + expect(calculatedScore).to.be.equal(expectedScore); + } + } + }); + + it('Manual verification of score function', async function () { + const minStake = await ParametersStorage.minimumStake(); + const maxStake = await ParametersStorage.maximumStake(); + const peerHash = ZERO_BYTES32; + const keyHash = HASH_RING_SIZE.div(4).toHexString(); + const distance = calculateDistance(peerHash, keyHash); + expect(distance).to.be.equal(HASH_RING_SIZE.div(4)); + + const maxDistance = HASH_RING_SIZE.div(2); + const stake = minStake.mul(2); + + const proximityScore = await calculateProximityScore(distance, stake, maxDistance, 1, 1); + expect(proximityScore).to.be.equal(BigNumber.from('500000000000000000')); + + const stakeScore = await calculateStakeScore(distance, stake, maxDistance, 1, 1, minStake, maxStake); + // 50k / 1950k = 0.0256410256410256410256410256410256410256410256410256410256410256 + expect(stakeScore).to.be.equal(BigNumber.from('25641025641025641')); + + const scoreBeforeScaling = await calculateScoreBeforeScaling( + distance, + stake, + maxDistance, + 1, + 1, + minStake, + maxStake, + ); + expect(scoreBeforeScaling).to.be.equal(BigNumber.from('525641025641025641')); + + const score = await calculateScore(distance, stake, maxDistance, 1, 1, minStake, maxStake); + expect(score).to.be.equal(BigNumber.from('288974209863')); + }); +}); diff --git a/test/v2/unit/ShardingTableV2.test.ts b/test/v2/unit/ShardingTableV2.test.ts new file mode 100644 index 00000000..28a2c939 --- /dev/null +++ b/test/v2/unit/ShardingTableV2.test.ts @@ -0,0 +1,264 @@ +import { randomBytes } from 'crypto'; + +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import { BytesLike, BigNumber } from 'ethers'; +import hre from 'hardhat'; + +import { HubController, Profile, ProfileStorage, ShardingTableStorageV2, ShardingTableV2 } from '../../../typechain'; + +type ShardingTableFixture = { + accounts: SignerWithAddress[]; + Profile: Profile; + ShardingTableStorage: ShardingTableStorageV2; + ShardingTable: ShardingTableV2; +}; + +type Node = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; +}; + +describe('@v2 @unit ShardingTableV2 contract', function () { + let accounts: SignerWithAddress[]; + let Profile: Profile; + let ShardingTableStorage: ShardingTableStorageV2; + let ShardingTable: ShardingTableV2; + let ProfileStorage: ProfileStorage; + let identityId: number; + + const nodeId1 = '0x01'; + const nodeId2 = '0x02'; + const nodeId3 = '0x03'; + const nodeId4 = '0x04'; + const nodeId5 = '0x05'; + + // 3 1 2 4 5 + + async function deployShardingTableFixture(): Promise { + await hre.deployments.fixture(['ShardingTableV2', 'IdentityStorageV2', 'StakingV2', 'Profile'], { + keepExistingDeployments: false, + }); + accounts = await hre.ethers.getSigners(); + const HubController = await hre.ethers.getContract('HubController'); + Profile = await hre.ethers.getContract('Profile'); + ShardingTableStorage = await hre.ethers.getContract('ShardingTableStorage'); + ShardingTable = await hre.ethers.getContract('ShardingTable'); + ProfileStorage = await hre.ethers.getContract('ProfileStorage'); + await HubController.setContractAddress('HubOwner', accounts[0].address); + + return { accounts, Profile, ShardingTableStorage, ShardingTable }; + } + + async function createProfile(operational: SignerWithAddress, admin: SignerWithAddress): Promise { + const OperationalProfile = Profile.connect(operational); + + const nodeId = '0x' + randomBytes(32).toString('hex'); + const sha256 = hre.ethers.utils.soliditySha256(['bytes'], [nodeId]); + + const receipt = await ( + await OperationalProfile.createProfile( + admin.address, + nodeId, + randomBytes(5).toString('hex'), + randomBytes(3).toString('hex'), + ) + ).wait(); + const identityId = Number(receipt.logs[0].topics[1]); + const blockchainNodeId = await ProfileStorage.getNodeId(identityId); + const blockchainSha256 = await ProfileStorage.getNodeAddress(identityId, 1); + + expect(blockchainNodeId).to.be.equal(nodeId); + expect(blockchainSha256).to.be.equal(sha256); + + return { + account: operational, + identityId, + nodeId, + sha256, + }; + } + + async function createMultipleRandomProfiles(count = 150): Promise { + const nodes = []; + + for (let i = 0; i < count; i++) { + const node = await createProfile(accounts[i], accounts[i + count]); + nodes.push(node); + } + + return nodes; + } + + async function createMultipleProfiles() { + const opWallet1 = Profile.connect(accounts[1]); + const opWallet2 = Profile.connect(accounts[2]); + const opWallet3 = Profile.connect(accounts[3]); + const opWallet4 = Profile.connect(accounts[4]); + const profile1 = await Profile.createProfile(accounts[6].address, nodeId1, 'Token', 'TKN'); + const profile2 = await opWallet1.createProfile(accounts[7].address, nodeId2, 'Token1', 'TKN1'); + const profile3 = await opWallet2.createProfile(accounts[8].address, nodeId3, 'Token2', 'TKN2'); + const profile4 = await opWallet3.createProfile(accounts[9].address, nodeId4, 'Token3', 'TKN3'); + const profile5 = await opWallet4.createProfile(accounts[10].address, nodeId5, 'Token4', 'TKN4'); + const idsArray = []; + + const profileArray = [profile1, profile2, profile3, profile4, profile5]; + for (const singleIdentityId of profileArray) { + const receipt = await singleIdentityId.wait(); + + identityId = receipt.events?.[3].args?.identityId.toNumber(); + idsArray.push(identityId); + } + return idsArray; + } + + async function validateShardingTableResult(identityIds: number[]) { + const nodesCount = (await ShardingTableStorage.nodesCount()).toNumber(); + + expect(identityIds.length).to.equal(nodesCount, 'Invalid number of nodes'); + + for (let i = 0; i < identityIds.length; i++) { + const node = await ShardingTableStorage.getNode(identityIds[i]); + const nodeByIndex = await ShardingTableStorage.getNodeByIndex(i); + + expect(node).to.be.eql(nodeByIndex); + + expect(node.identityId.toNumber()).to.equal(identityIds[i], 'Invalid node on this position'); + + expect(node.index.toNumber()).to.equal(i, 'Invalid node index'); + } + + const shardingTable = (await ShardingTable['getShardingTable()']()).map((node) => node.identityId.toNumber()); + + expect(shardingTable).to.be.eql(identityIds); + } + + beforeEach(async () => { + hre.helpers.resetDeploymentsJson(); + ({ accounts, Profile, ShardingTableStorage, ShardingTable } = await loadFixture(deployShardingTableFixture)); + }); + + it('Should initialize contract with correct values', async () => { + const name = await ShardingTable.name(); + const version = await ShardingTable.version(); + + expect(name).to.equal('ShardingTable'); + expect(version).to.equal('2.0.0'); + }); + + it('Insert 5 nodes, nodes are sorted expect to pass', async () => { + const profiles = await createMultipleProfiles(); + + // 2 + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); + await validateShardingTableResult([2]); + + // 3 2 + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[2]); + await validateShardingTableResult([3, 2]); + + // 3 2 5 + await ShardingTable['insertNode(uint72,uint72)'](2, profiles[4]); + await validateShardingTableResult([3, 2, 5]); + + // 3 2 4 5 + await ShardingTable['insertNode(uint72,uint72)'](2, profiles[3]); + await validateShardingTableResult([3, 2, 4, 5]); + + // 3 1 2 4 5 + await ShardingTable['insertNode(uint72,uint72)'](1, profiles[0]); + await validateShardingTableResult([3, 1, 2, 4, 5]); + }); + + it('Insert 5 nodes, without sorting, expect to be pass and be sorted on insert', async () => { + const profiles = await createMultipleProfiles(); + + // 2 + await ShardingTable['insertNode(uint72)'](profiles[1]); + await validateShardingTableResult([2]); + + // 3 2 + await ShardingTable['insertNode(uint72)'](profiles[2]); + await validateShardingTableResult([3, 2]); + + // 3 2 5 + await ShardingTable['insertNode(uint72)'](profiles[4]); + await validateShardingTableResult([3, 2, 5]); + + // 3 2 4 5 + await ShardingTable['insertNode(uint72)'](profiles[3]); + await validateShardingTableResult([3, 2, 4, 5]); + + // 3 1 2 4 5 + await ShardingTable['insertNode(uint72)'](profiles[0]); + await validateShardingTableResult([3, 1, 2, 4, 5]); + }); + + it('Insert 100 nodes to the beginning of the Sharding Table, expect gas consumption to fit in 1_000_000 wei', async () => { + const gasConsumptionThreshold = hre.ethers.utils.parseEther('1000000'); + + const nodes = await createMultipleRandomProfiles(100); + + const sortedNodes = nodes.sort((a, b) => { + const aBN = BigNumber.from(a.sha256); + const bBN = BigNumber.from(b.sha256); + if (bBN.eq(aBN)) { + return 0; + } + return bBN.lt(aBN) ? -1 : 1; + }); + + for (const node of sortedNodes) { + const tx = await ShardingTable['insertNode(uint72,uint72)'](0, node.identityId); + const receipt = await tx.wait(); + expect(receipt.gasUsed).to.be.lessThanOrEqual(gasConsumptionThreshold); + } + }); + + it('Insert node with invalid prevIdentityId expect to fail', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); + + await expect(ShardingTable['insertNode(uint72,uint72)'](1, profiles[0])).to.be.revertedWithCustomError( + ShardingTable, + 'InvalidIndexWithRespectToPreviousNode', + ); + }); + + it('Insert node with invalid nextIdentityId expect to fail', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); + + await expect(ShardingTable['insertNode(uint72,uint72)'](0, profiles[3])).to.be.revertedWithCustomError( + ShardingTable, + 'InvalidIndexWithRespectToNextNode', + ); + }); + + it('Remove node from sharding table, expect to pass', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[2]); + await ShardingTable['insertNode(uint72,uint72)'](1, profiles[0]); + await ShardingTable['insertNode(uint72,uint72)'](2, profiles[1]); + await ShardingTable['insertNode(uint72,uint72)'](3, profiles[3]); + await ShardingTable['insertNode(uint72,uint72)'](4, profiles[4]); + + // remove from index 0 + await ShardingTable.removeNode(profiles[2]); + await validateShardingTableResult([1, 2, 4, 5]); + + // remove from last index + await ShardingTable.removeNode(profiles[4]); + await validateShardingTableResult([1, 2, 4]); + + // remove from middle + await ShardingTable.removeNode(profiles[1]); + await validateShardingTableResult([1, 4]); + }); +}); diff --git a/test/v2/unit/StakingV2.test.ts b/test/v2/unit/StakingV2.test.ts new file mode 100644 index 00000000..f88a8dfd --- /dev/null +++ b/test/v2/unit/StakingV2.test.ts @@ -0,0 +1,533 @@ +import { randomBytes } from 'crypto'; + +import { loadFixture, time } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import { BigNumber, BytesLike } from 'ethers'; +import hre from 'hardhat'; + +import { + Token, + Profile, + ServiceAgreementStorageV1U1, + StakingStorage, + HubController, + StakingV2, + Shares, + ParametersStorage, + ProfileStorage, +} from '../../../typechain'; + +type StakingFixture = { + accounts: SignerWithAddress[]; + Token: Token; + Profile: Profile; + ServiceAgreementStorageV1U1: ServiceAgreementStorageV1U1; + StakingV2: StakingV2; + StakingStorage: StakingStorage; +}; + +type Node = { + account: SignerWithAddress; + identityId: number; + nodeId: BytesLike; + sha256: BytesLike; +}; + +describe('@v2 @unit StakingV2 contract', function () { + let accounts: SignerWithAddress[]; + let ParametersStorage: ParametersStorage; + let ProfileStorage: ProfileStorage; + let StakingV2: StakingV2; + let StakingStorage: StakingStorage; + let Token: Token; + let Profile: Profile; + let ServiceAgreementStorageV1U1: ServiceAgreementStorageV1U1; + const identityId1 = 1; + const totalStake = hre.ethers.utils.parseEther('1000'); + const operatorFee = hre.ethers.BigNumber.from(10); + const transferAmount = hre.ethers.utils.parseEther('100'); + const timestamp = 1674261619; + + async function deployStakingFixture(): Promise { + await hre.deployments.fixture(['StakingV2', 'Profile']); + ParametersStorage = await hre.ethers.getContract('ParametersStorage'); + ProfileStorage = await hre.ethers.getContract('ProfileStorage'); + StakingV2 = await hre.ethers.getContract('Staking'); + StakingStorage = await hre.ethers.getContract('StakingStorage'); + Token = await hre.ethers.getContract('Token'); + Profile = await hre.ethers.getContract('Profile'); + ServiceAgreementStorageV1U1 = await hre.ethers.getContract( + 'ServiceAgreementStorageV1U1', + ); + accounts = await hre.ethers.getSigners(); + const HubController = await hre.ethers.getContract('HubController'); + await HubController.setContractAddress('HubOwner', accounts[0].address); + await HubController.setContractAddress('NotHubOwner', accounts[1].address); + + return { accounts, Token, Profile, ServiceAgreementStorageV1U1, StakingV2, StakingStorage }; + } + + async function createProfile(operational: SignerWithAddress, admin: SignerWithAddress): Promise { + const OperationalProfile = Profile.connect(operational); + + const nodeId = '0x' + randomBytes(32).toString('hex'); + const sha256 = hre.ethers.utils.soliditySha256(['bytes'], [nodeId]); + + const receipt = await ( + await OperationalProfile.createProfile( + admin.address, + nodeId, + randomBytes(5).toString('hex'), + randomBytes(3).toString('hex'), + ) + ).wait(); + const identityId = Number(receipt.logs[0].topics[1]); + const blockchainNodeId = await ProfileStorage.getNodeId(identityId); + const blockchainSha256 = await ProfileStorage.getNodeAddress(identityId, 1); + + expect(blockchainNodeId).to.be.equal(nodeId); + expect(blockchainSha256).to.be.equal(sha256); + + await OperationalProfile.setAsk(identityId, hre.ethers.utils.parseEther('0.25')); + + return { + account: operational, + identityId, + nodeId, + sha256, + }; + } + + async function calculateSharesToMint(newStakeAmount: BigNumber, totalShares: BigNumber): Promise { + const totalStake = await Token.balanceOf(StakingStorage.address); + if (totalStake.isZero()) { + return newStakeAmount; + } else { + return newStakeAmount.mul(totalShares).div(totalStake); + } + } + + async function calculateEligibleTokens(heldShares: BigNumber, totalShares: BigNumber): Promise { + const totalStake = await Token.balanceOf(StakingStorage.address); + return heldShares.mul(totalStake).div(totalShares); + } + + beforeEach(async () => { + hre.helpers.resetDeploymentsJson(); + ({ accounts, Token, Profile, ServiceAgreementStorageV1U1, StakingV2, StakingStorage } = await loadFixture( + deployStakingFixture, + )); + }); + + it('The contract is named "Staking"', async () => { + expect(await StakingV2.name()).to.equal('Staking'); + }); + + it('The contract is version "2.0.0"', async () => { + expect(await StakingV2.version()).to.equal('2.0.0'); + }); + + it('Non-Contract should not be able to setTotalStake; expect to fail', async () => { + const StakingStorageWithNonHubOwner = StakingStorage.connect(accounts[2]); + await expect(StakingStorageWithNonHubOwner.setTotalStake(identityId1, totalStake)).to.be.revertedWith( + 'Fn can only be called by the hub', + ); + }); + + it('Contract should be able to setTotalStake; expect to pass', async () => { + await StakingStorage.setTotalStake(identityId1, totalStake); + expect(await StakingStorage.totalStakes(identityId1)).to.equal(totalStake); + }); + + it('Non-Contract should not be able to setOperatorFee; expect to fail', async () => { + const StakingStorageWithNonHubOwner = StakingStorage.connect(accounts[2]); + await expect(StakingStorageWithNonHubOwner.setOperatorFee(identityId1, operatorFee)).to.be.revertedWith( + 'Fn can only be called by the hub', + ); + }); + + it('Contract should be able to setOperatorFee; expect to pass', async () => { + await StakingStorage.setOperatorFee(identityId1, operatorFee); + expect(await StakingStorage.operatorFees(identityId1)).to.equal(operatorFee); + }); + + it('Non-Contract should not be able to createWithdrawalRequest; expect to fail', async () => { + const StakingStorageWithNonHubOwner = StakingStorage.connect(accounts[2]); + await expect( + StakingStorageWithNonHubOwner.createWithdrawalRequest(identityId1, accounts[2].address, totalStake, 2022), + ).to.be.revertedWith('Fn can only be called by the hub'); + }); + + it('Contract should be able to createWithdrawalRequest; expect to pass', async () => { + await StakingStorage.createWithdrawalRequest(identityId1, accounts[1].address, totalStake, timestamp); + + expect(await StakingStorage.withdrawalRequestExists(identityId1, accounts[1].address)).to.equal(true); + expect(await StakingStorage.getWithdrawalRequestAmount(identityId1, accounts[1].address)).to.equal(totalStake); + expect(await StakingStorage.getWithdrawalRequestTimestamp(identityId1, accounts[1].address)).to.equal(timestamp); + }); + + it('Non-Contract should not be able to deleteWithdrawalRequest; expect to fail', async () => { + const StakingStorageWithNonHubOwner = StakingStorage.connect(accounts[2]); + await expect( + StakingStorageWithNonHubOwner.deleteWithdrawalRequest(identityId1, accounts[2].address), + ).to.be.revertedWith('Fn can only be called by the hub'); + }); + + it('Contract should be able to deleteWithdrawalRequest; expect to pass', async () => { + await StakingStorage.createWithdrawalRequest(identityId1, accounts[1].address, totalStake, timestamp); + + await StakingStorage.deleteWithdrawalRequest(identityId1, accounts[1].address); + expect(await StakingStorage.withdrawalRequestExists(identityId1, accounts[1].address)).to.equal(false); + expect(await StakingStorage.getWithdrawalRequestAmount(identityId1, accounts[1].address)).to.equal(0); + expect(await StakingStorage.getWithdrawalRequestTimestamp(identityId1, accounts[1].address)).to.equal(0); + }); + + it('Non-Contract should not be able to transferStake; expect to fail', async () => { + const StakingStorageWithNonHubOwner = StakingStorage.connect(accounts[2]); + await expect(StakingStorageWithNonHubOwner.transferStake(accounts[2].address, transferAmount)).to.be.revertedWith( + 'Fn can only be called by the hub', + ); + }); + + it('Contract should be able to transferStake; expect to pass', async () => { + await Token.mint(StakingStorage.address, hre.ethers.utils.parseEther(`${2_000_000}`)); + + const initialReceiverBalance = await Token.balanceOf(accounts[1].address); + await StakingStorage.transferStake(accounts[1].address, transferAmount); + + expect(await Token.balanceOf(accounts[1].address)).to.equal(initialReceiverBalance.add(transferAmount)); + }); + + it('Create 1 node; expect that stake is created and correctly set', async () => { + await Token.increaseAllowance(StakingV2.address, hre.ethers.utils.parseEther(`${2_000_000}`)); + + const nodeId1 = '0x07f38512786964d9e70453371e7c98975d284100d44bd68dab67fe00b525cb66'; + await Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN'); + + await StakingV2.connect(accounts[1])['addStake(address,uint72,uint96)']( + accounts[0].address, + identityId1, + hre.ethers.utils.parseEther(`${2_000_000}`), + ); + expect(await StakingStorage.totalStakes(identityId1)).to.equal( + hre.ethers.utils.parseEther(`${2_000_000}`), + 'Total amount of stake is not set', + ); + }); + + it('Add reward; expect that total stake is increased', async () => { + await Token.mint(ServiceAgreementStorageV1U1.address, hre.ethers.utils.parseEther(`${2_000_000}`)); + const nodeId1 = '0x07f38512786964d9e70453371e7c98975d284100d44bd68dab67fe00b525cb66'; + await Profile.createProfile(accounts[1].address, nodeId1, 'Token', 'TKN'); + + const agreementId = '0x' + randomBytes(32).toString('hex'); + const startTime = Math.floor(Date.now() / 1000).toString(); + const epochsNumber = 5; + const epochLength = 10; + const tokenAmount = hre.ethers.utils.parseEther('100'); + const scoreFunctionId = 0; + const proofWindowOffsetPerc = 10; + + await ServiceAgreementStorageV1U1.createServiceAgreementObject( + agreementId, + startTime, + epochsNumber, + epochLength, + tokenAmount, + scoreFunctionId, + proofWindowOffsetPerc, + ); + + await StakingV2.connect(accounts[1]).addReward( + agreementId, + identityId1, + hre.ethers.utils.parseEther(`${2_000_000}`), + ); + expect(await StakingStorage.totalStakes(identityId1)).to.equal( + hre.ethers.utils.parseEther(`${2_000_000}`), + 'Total amount of stake is not increased after adding reward', + ); + }); + + it('New profile created, stake added by node admin, StakeIncreased/SharesMinted events are emitted, token/shares balances are correct', async () => { + const node = await createProfile(accounts[0], accounts[1]); + + const minStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.minimumStake())); + const maxStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.maximumStake())); + const stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - minStake + 1)) + minStake}`, + ); + + const initialBalance = await Token.balanceOf(accounts[1].address); + const oldStake = await StakingStorage.totalStakes(node.identityId); + + const sharesAddress = await ProfileStorage.getSharesContractAddress(node.identityId); + const SharesContract = await hre.ethers.getContractAt('Shares', sharesAddress); + const initialSharesBalance = await SharesContract.balanceOf(accounts[1].address); + const sharesTotalSupply = await SharesContract.totalSupply(); + const sharesToMint = await calculateSharesToMint(stakeAmount, sharesTotalSupply); + + await Token.connect(accounts[1]).increaseAllowance(StakingV2.address, stakeAmount); + + expect(await StakingV2.connect(accounts[1])['addStake(uint72,uint96)'](node.identityId, stakeAmount)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs(node.identityId, node.nodeId, accounts[1], oldStake, oldStake.add(stakeAmount)) + .to.emit(StakingV2, 'SharesMinted') + .withArgs(sharesAddress, accounts[1].address, sharesToMint, sharesTotalSupply.add(sharesToMint)); + + const finalBalance = await Token.balanceOf(accounts[1].address); + const finalSharesBalance = await SharesContract.balanceOf(accounts[1].address); + + expect(finalBalance).to.be.equal(initialBalance.sub(stakeAmount)); + expect(finalSharesBalance).to.be.equal(initialSharesBalance.add(sharesToMint)); + }); + + it('New profile created, stake added by 5 delegators, StakeIncreased/SharesMinted events are emitted, token/shares balances are correct', async () => { + const node = await createProfile(accounts[0], accounts[1]); + + const minStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.minimumStake())); + const maxStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.maximumStake())); + let stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - minStake + 1)) + minStake}`, + ); + + let initialBalance = await Token.balanceOf(accounts[1].address); + let oldStake = await StakingStorage.totalStakes(node.identityId); + + const sharesAddress = await ProfileStorage.getSharesContractAddress(node.identityId); + const SharesContract = await hre.ethers.getContractAt('Shares', sharesAddress); + let initialSharesBalance = await SharesContract.balanceOf(accounts[1].address); + let sharesTotalSupply = await SharesContract.totalSupply(); + let sharesToMint = await calculateSharesToMint(stakeAmount, sharesTotalSupply); + + await Token.connect(accounts[1]).increaseAllowance(StakingV2.address, stakeAmount); + + expect(await StakingV2.connect(accounts[1])['addStake(uint72,uint96)'](node.identityId, stakeAmount)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs(node.identityId, node.nodeId, accounts[1], oldStake, oldStake.add(stakeAmount)) + .to.emit(StakingV2, 'SharesMinted') + .withArgs(sharesAddress, accounts[1].address, sharesToMint, sharesTotalSupply.add(sharesToMint)); + + let finalBalance = await Token.balanceOf(accounts[1].address); + let finalSharesBalance = await SharesContract.balanceOf(accounts[1].address); + + expect(finalBalance).to.be.equal(initialBalance.sub(stakeAmount)); + expect(finalSharesBalance).to.be.equal(initialSharesBalance.add(sharesToMint)); + + for (let i = 2; i < 7; i += 1) { + oldStake = await StakingStorage.totalStakes(node.identityId); + stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - Number(hre.ethers.utils.formatEther(oldStake))))}`, + ); + sharesTotalSupply = await SharesContract.totalSupply(); + sharesToMint = await calculateSharesToMint(stakeAmount, sharesTotalSupply); + + initialBalance = await Token.balanceOf(accounts[i].address); + initialSharesBalance = await SharesContract.balanceOf(accounts[i].address); + + await Token.connect(accounts[i]).increaseAllowance(StakingV2.address, stakeAmount); + + expect(await StakingV2.connect(accounts[i])['addStake(uint72,uint96)'](node.identityId, stakeAmount)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs(node.identityId, node.nodeId, accounts[1], oldStake, oldStake.add(stakeAmount)) + .to.emit(StakingV2, 'SharesMinted') + .withArgs(sharesAddress, accounts[i].address, sharesToMint, sharesTotalSupply.add(sharesToMint)); + + finalBalance = await Token.balanceOf(accounts[i].address); + finalSharesBalance = await SharesContract.balanceOf(accounts[i].address); + + expect(finalBalance).to.be.equal(initialBalance.sub(stakeAmount)); + expect(finalSharesBalance).to.be.equal(initialSharesBalance.add(sharesToMint)); + } + }); + + it('New profile created, stake added by node runner and 5 delegators, operator fee change triggered, teleport, operator fee changed, reward added, stake withdrawn, events and balance are correct', async () => { + const node = await createProfile(accounts[0], accounts[1]); + + const minStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.minimumStake())); + const maxStake = Number(hre.ethers.utils.formatEther(await ParametersStorage.maximumStake())); + let stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - minStake + 1)) + minStake}`, + ); + + let initialBalance = await Token.balanceOf(accounts[1].address); + let oldStake = await StakingStorage.totalStakes(node.identityId); + + const sharesAddress = await ProfileStorage.getSharesContractAddress(node.identityId); + const SharesContract = await hre.ethers.getContractAt('Shares', sharesAddress); + let initialSharesBalance = await SharesContract.balanceOf(accounts[1].address); + let sharesTotalSupply = await SharesContract.totalSupply(); + let sharesToMint = await calculateSharesToMint(stakeAmount, sharesTotalSupply); + + await Token.connect(accounts[1]).increaseAllowance(StakingV2.address, stakeAmount); + + expect(await StakingV2.connect(accounts[1])['addStake(uint72,uint96)'](node.identityId, stakeAmount)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs(node.identityId, node.nodeId, accounts[1], oldStake, oldStake.add(stakeAmount)) + .to.emit(StakingV2, 'SharesMinted') + .withArgs(sharesAddress, accounts[1].address, sharesToMint, sharesTotalSupply.add(sharesToMint)); + + let finalBalance = await Token.balanceOf(accounts[1].address); + let finalSharesBalance = await SharesContract.balanceOf(accounts[1].address); + + expect(finalBalance).to.be.equal(initialBalance.sub(stakeAmount)); + expect(finalSharesBalance).to.be.equal(initialSharesBalance.add(sharesToMint)); + + for (let i = 2; i < 7; i += 1) { + oldStake = await StakingStorage.totalStakes(node.identityId); + stakeAmount = hre.ethers.utils.parseEther( + `${Math.floor(Math.random() * (maxStake - Number(hre.ethers.utils.formatEther(oldStake))))}`, + ); + sharesTotalSupply = await SharesContract.totalSupply(); + sharesToMint = await calculateSharesToMint(stakeAmount, sharesTotalSupply); + + initialBalance = await Token.balanceOf(accounts[i].address); + initialSharesBalance = await SharesContract.balanceOf(accounts[i].address); + + await Token.connect(accounts[i]).increaseAllowance(StakingV2.address, stakeAmount); + + expect(await StakingV2.connect(accounts[i])['addStake(uint72,uint96)'](node.identityId, stakeAmount)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs(node.identityId, node.nodeId, accounts[1].address, oldStake, oldStake.add(stakeAmount)) + .to.emit(StakingV2, 'SharesMinted') + .withArgs(sharesAddress, accounts[i].address, sharesToMint, sharesTotalSupply.add(sharesToMint)); + + finalBalance = await Token.balanceOf(accounts[i].address); + finalSharesBalance = await SharesContract.balanceOf(accounts[i].address); + + expect(finalBalance).to.be.equal(initialBalance.sub(stakeAmount)); + expect(finalSharesBalance).to.be.equal(initialSharesBalance.add(sharesToMint)); + } + + const newOperatorFee = 50; + const stakeWithdrawalDelay = await ParametersStorage.stakeWithdrawalDelay(); + let blockTimestamp = (await hre.ethers.provider.getBlock('latest')).timestamp; + expect(await StakingV2.connect(accounts[1]).startOperatorFeeChange(node.identityId, newOperatorFee)) + .to.emit(StakingV2, 'OperatorFeeChangeStarted') + .withArgs(node.identityId, node.nodeId, newOperatorFee, blockTimestamp + stakeWithdrawalDelay); + + await time.increaseTo(blockTimestamp + stakeWithdrawalDelay); + + expect(await StakingV2.connect(accounts[1]).finishOperatorFeeChange(node.identityId)) + .to.emit(StakingV2, 'OperatorFeeChangeFinished') + .withArgs(node.identityId, node.nodeId, newOperatorFee); + + const agreementId = '0x' + randomBytes(32).toString('hex'); + const startTime = Math.floor(Date.now() / 1000).toString(); + const epochsNumber = 5; + const epochLength = 10; + const tokenAmount = hre.ethers.utils.parseEther('100'); + const scoreFunctionId = 0; + const proofWindowOffsetPerc = 10; + + const reward = hre.ethers.utils.parseEther(`${2_000_000}`); + await Token.mint(ServiceAgreementStorageV1U1.address, reward); + + await ServiceAgreementStorageV1U1.createServiceAgreementObject( + agreementId, + startTime, + epochsNumber, + epochLength, + tokenAmount, + scoreFunctionId, + proofWindowOffsetPerc, + ); + + const oldAccOperatorFee = await ProfileStorage.getAccumulatedOperatorFee(node.identityId); + const accOperatorFee = reward.mul(newOperatorFee).div(100); + const delegatorsReward = reward.sub(accOperatorFee); + oldStake = await StakingStorage.totalStakes(node.identityId); + + expect(await StakingV2.addReward(agreementId, node.identityId, reward)) + .to.emit(StakingV2, 'AccumulatedOperatorFeeIncreased') + .withArgs(node.identityId, node.nodeId, oldAccOperatorFee, oldAccOperatorFee.add(accOperatorFee)) + .to.emit(StakingV2, 'StakeIncreased') + .withArgs( + node.identityId, + node.nodeId, + ServiceAgreementStorageV1U1.address, + oldStake, + oldStake.add(delegatorsReward), + ) + .to.emit(StakingV2, 'RewardCollected') + .withArgs(node.identityId, node.nodeId, ServiceAgreementStorageV1U1.address, accOperatorFee, delegatorsReward); + + initialBalance = await Token.balanceOf(accounts[1].address); + oldStake = await StakingStorage.totalStakes(node.identityId); + + initialSharesBalance = await SharesContract.balanceOf(accounts[1].address); + sharesTotalSupply = await SharesContract.totalSupply(); + let eligibleTokens = await calculateEligibleTokens(initialSharesBalance, sharesTotalSupply); + + await SharesContract.connect(accounts[1]).increaseAllowance(StakingV2.address, initialSharesBalance); + + blockTimestamp = (await hre.ethers.provider.getBlock('latest')).timestamp; + expect(await StakingV2.connect(accounts[1]).startStakeWithdrawal(node.identityId, initialSharesBalance)) + .to.emit(StakingV2, 'StakeWithdrawalStarted') + .withArgs( + node.identityId, + node.nodeId, + accounts[1].address, + oldStake, + oldStake.add(eligibleTokens), + blockTimestamp + stakeWithdrawalDelay, + ) + .to.emit(StakingV2, 'SharesBurned') + .withArgs(sharesAddress, accounts[1].address, initialSharesBalance, sharesTotalSupply.sub(initialSharesBalance)); + + await time.increaseTo(blockTimestamp + stakeWithdrawalDelay); + + expect(await StakingV2.connect(accounts[1]).withdrawStake(node.identityId)) + .to.emit(StakingV2, 'StakeWithdrawn') + .withArgs(node.identityId, node.nodeId, accounts[1].address, eligibleTokens); + + finalBalance = await Token.balanceOf(accounts[1].address); + finalSharesBalance = await SharesContract.balanceOf(accounts[1].address); + + expect(finalBalance).to.be.equal(initialBalance.add(eligibleTokens)); + expect(finalSharesBalance).to.be.equal(0); + + for (let i = 2; i < 7; i += 1) { + initialBalance = await Token.balanceOf(accounts[i].address); + oldStake = await StakingStorage.totalStakes(node.identityId); + + initialSharesBalance = await SharesContract.balanceOf(accounts[i].address); + sharesTotalSupply = await SharesContract.totalSupply(); + eligibleTokens = await calculateEligibleTokens(initialSharesBalance, sharesTotalSupply); + + await SharesContract.connect(accounts[i]).increaseAllowance(StakingV2.address, initialSharesBalance); + + blockTimestamp = (await hre.ethers.provider.getBlock('latest')).timestamp; + expect(await StakingV2.connect(accounts[i]).startStakeWithdrawal(node.identityId, initialSharesBalance)) + .to.emit(StakingV2, 'StakeWithdrawalStarted') + .withArgs( + node.identityId, + node.nodeId, + accounts[i].address, + oldStake, + oldStake.add(eligibleTokens), + blockTimestamp + stakeWithdrawalDelay, + ) + .to.emit(StakingV2, 'SharesBurned') + .withArgs( + sharesAddress, + accounts[i].address, + initialSharesBalance, + sharesTotalSupply.sub(initialSharesBalance), + ); + + await time.increaseTo(blockTimestamp + stakeWithdrawalDelay); + + expect(await StakingV2.connect(accounts[i]).withdrawStake(node.identityId)) + .to.emit(StakingV2, 'StakeWithdrawn') + .withArgs(node.identityId, node.nodeId, accounts[i].address, eligibleTokens); + + finalBalance = await Token.balanceOf(accounts[i].address); + finalSharesBalance = await SharesContract.balanceOf(accounts[i].address); + + expect(finalBalance).to.be.equal(initialBalance.add(eligibleTokens)); + expect(finalSharesBalance).to.be.equal(0); + } + }); +});