From 13ea0555183ad8ade589584807b55d094e7bb7d4 Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Tue, 23 Jan 2024 12:49:09 +0100 Subject: [PATCH 01/46] Updated sharding table logic contract and sharding table storage contract --- contracts/v2/ShardingTable.sol | 218 ++++++++++++++++++ contracts/v2/storage/ShardingTableStorage.sol | 112 +++++++++ contracts/v2/structs/ShardingTableStructs.sol | 19 ++ 3 files changed, 349 insertions(+) create mode 100644 contracts/v2/ShardingTable.sol create mode 100644 contracts/v2/storage/ShardingTableStorage.sol create mode 100644 contracts/v2/structs/ShardingTableStructs.sol diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol new file mode 100644 index 00000000..cd264557 --- /dev/null +++ b/contracts/v2/ShardingTable.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; +import {ShardingTableStorage} 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 "./interface/Named.sol"; +import {Versioned} from "./interface/Versioned.sol"; +import {ShardingTableStructs} from "./structs/ShardingTableStructs.sol"; + +//import {NULL} from "../v1/constants/ShardingTableConstants.sol"; + +contract ShardingTable 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 = "1.0.1"; + + ProfileStorage public profileStorage; + ShardingTableStorage 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 = ShardingTableStorage(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 (ShardingTableStructs.NodeInfo[] memory) { + return _getShardingTable(startingIdentityId, nodesNumber); + } + + function getShardingTable() external view returns (ShardingTableStructs.NodeInfo[] memory) { + ShardingTableStorage sts = shardingTableStorage; + return _getShardingTable(sts.head(), sts.nodesCount()); + } + + // + // function pushBack(uint72 identityId) external onlyContracts { + // ProfileStorage ps = profileStorage; + // require(ps.profileExists(identityId), "Profile doesn't exist"); + // + // ShardingTableStorage sts = shardingTableStorage; + // + // sts.createNodeObject(identityId, NULL, NULL); + // + // if (sts.tail() != NULL) sts.link(sts.tail(), identityId); + // + // sts.setTail(identityId); + // + // if (sts.head() == NULL) sts.setHead(identityId); + // + // sts.incrementNodesCount(); + // + // emit NodeAdded( + // identityId, + // ps.getNodeId(identityId), + // ps.getAsk(identityId), + // stakingStorage.totalStakes(identityId) + // ); + // } + // + // function pushFront(uint72 identityId) external onlyContracts { + // ProfileStorage ps = profileStorage; + // require(ps.profileExists(identityId), "Profile doesn't exist"); + // + // ShardingTableStorage sts = shardingTableStorage; + // + // sts.createNodeObject(identityId, NULL, NULL); + // + // if (sts.head() != NULL) sts.link(identityId, sts.head()); + // + // shardingTableStorage.setHead(identityId); + // + // if (sts.tail() == NULL) sts.setTail(identityId); + // + // sts.incrementNodesCount(); + // + // emit NodeAdded( + // identityId, + // ps.getNodeId(identityId), + // ps.getAsk(identityId), + // stakingStorage.totalStakes(identityId) + // ); + // } + + function insertNode(uint72 identityId, uint72 previousIdentityId, uint72 nextIdentityId) external onlyContracts { + ProfileStorage ps = profileStorage; + require(ps.profileExists(identityId), "Profile doesn't exist"); + + ShardingTableStorage sts = shardingTableStorage; + + ShardingTableStructs.Node previousNode = sts.getNode(previousIdentityId); + + ShardingTableStructs.Node nextNode = sts.getNode(nextIdentityId); + + if ( + previousIdentityId == identityId || + (previousIdentityId > identityId && previousNode.index != sts.nodesCount) + ) { + revert("Invalid previous node id"); + } + + if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { + revert("Invalid next node id"); + } + + if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { + revert("Invalid previous and next node id"); + } + + sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); + + for (uint i = nextNode.index + 1; i < sts.nodesCount - 1; i++) { + nextNode.index = i; + nextNode = sts.getNode(nextNode.nextIdentityId); + } + + sts.link(previousIdentityId, identityId, nextIdentityId); + + sts.incrementNodesCount(); + + emit NodeAdded( + identityId, + ps.getNodeId(identityId), + ps.getAsk(identityId), + stakingStorage.totalStakes(identityId) + ); + } + + function removeNode(uint72 identityId) external onlyContracts { + ProfileStorage ps = profileStorage; + require(ps.profileExists(identityId), "Profile doesn't exist"); + + ShardingTableStorage sts = shardingTableStorage; + + ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); + + ShardingTableStructs.Node nextNode = sts.getNode(nodeToRemove.nextIdentityId); + + sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); + + for (uint i = nodeToRemove.index; i < sts.nodesCount - 1; i++) { + nextNode.index = i; + nextNode = sts.getNode(nextNode.nextIdentityId); + } + + sts.deleteNodeObject(identityId); + sts.decrementNodesCount(); + + emit NodeRemoved(identityId, ps.getNodeId(identityId)); + } + + function _getShardingTable( + uint72 startingIdentityId, + uint72 nodesNumber + ) internal view virtual returns (ShardingTableStructs.NodeInfo[] memory) { + ShardingTableStructs.NodeInfo[] memory nodesPage; + ShardingTableStorage sts = shardingTableStorage; + + if ((sts.nodesCount() == 0) || (nodesNumber == 0)) { + return nodesPage; + } + + ShardingTableStructs.Node memory startingNode = sts.getNode(startingIdentityId); + + require((startingIdentityId == NULL) || (startingNode.identityId != NULL), "Wrong starting Identity ID"); + + nodesPage = new ShardingTableStructs.NodeInfo[](nodesNumber); + + ProfileStorage ps = profileStorage; + StakingStorage ss = stakingStorage; + + nodesPage[0] = ShardingTableStructs.NodeInfo({ + nodeId: ps.getNodeId(startingIdentityId), + identityId: startingIdentityId, + ask: ps.getAsk(startingNode.identityId), + stake: ss.totalStakes(startingNode.identityId) + }); + + uint72 nextIdentityId = startingIdentityId; + uint72 i = 1; + while ((i < nodesNumber) && (nextIdentityId != NULL)) { + nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + + nodesPage[i] = ShardingTableStructs.NodeInfo({ + nodeId: ps.getNodeId(nextIdentityId), + identityId: nextIdentityId, + ask: ps.getAsk(nextIdentityId), + stake: ss.totalStakes(nextIdentityId) + }); + + unchecked { + i += 1; + } + } + + return nodesPage; + } +} diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol new file mode 100644 index 00000000..81e4625b --- /dev/null +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT + +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 {NULL} from "../constants/ShardingTableConstants.sol"; + +contract ShardingTableStorage is Named, Versioned, HubDependent { + string private constant _NAME = "ShardingTableStorage"; + string private constant _VERSION = "2.0.0"; + + uint72 public head; + uint72 public tail; + uint72 public nodesCount; + + // identityId => Node + mapping(uint72 => ShardingTableStructs.Node) internal nodes; + + constructor(address hubAddress) HubDependent(hubAddress) { + head = NULL; + tail = NULL; + } + + 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 setHead(uint72 identityId) external onlyContracts { + head = identityId; + } + + function setTail(uint72 identityId) external onlyContracts { + tail = identityId; + } + + function createNodeObject( + uint72 identityId, + uint72 prevIdentityId, + uint72 nextIdentityId, + uint72 index + ) external onlyContracts { + nodes[identityId] = ShardingTableStructs.Node({ + identityId: identityId, + index: index, + prevIdentityId: prevIdentityId, + nextIdentityId: nextIdentityId + }); + } + + function getNode(uint72 identityId) external view returns (ShardingTableStructs.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 setPrevIdentityId(uint72 identityId, uint72 newPrevIdentityId) external onlyContracts { + nodes[identityId].prevIdentityId = newPrevIdentityId; + } + + function setNextIdentityId(uint72 identityId, uint72 newNextIdentityId) external onlyContracts { + nodes[identityId].nextIdentityId = newNextIdentityId; + } + + function getMultipleNodes( + uint72 firstIdentityId, + uint16 nodesNumber + ) external view returns (ShardingTableStructs.Node[] memory) { + ShardingTableStructs.Node[] memory nodesPage = new ShardingTableStructs.Node[](nodesNumber); + + ShardingTableStructs.Node memory currentNode = nodes[firstIdentityId]; + for (uint256 i; i < nodesNumber; ) { + nodesPage[i] = currentNode; + currentNode = nodes[currentNode.nextIdentityId]; + unchecked { + i++; + } + } + + return nodesPage; + } + + function link(uint72 leftNodeIdentityId, uint72 newIdentityId, uint72 rightNodeIdentityId) external onlyContracts { + nodes[leftNodeIdentityId].nextIdentityId = newIdentityId; + nodes[rightNodeIdentityId].prevIdentityId = newIdentityId; + } + + function link(uint72 leftNodeIdentityId, uint72 rightNodeIdentityId) external onlyContracts { + nodes[leftNodeIdentityId].nextIdentityId = rightNodeIdentityId; + nodes[rightNodeIdentityId].prevIdentityId = leftNodeIdentityId; + } +} diff --git a/contracts/v2/structs/ShardingTableStructs.sol b/contracts/v2/structs/ShardingTableStructs.sol new file mode 100644 index 00000000..f7f15554 --- /dev/null +++ b/contracts/v2/structs/ShardingTableStructs.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ShardingTableStructs { + struct NodeInfo { + bytes nodeId; + uint72 identityId; + uint96 ask; + uint96 stake; + } + + struct Node { + uint72 identityId; + uint72 index; + uint72 prevIdentityId; + uint72 nextIdentityId; + } +} From ae8cc06056023f835acc1c7118316b73ca5c7089 Mon Sep 17 00:00:00 2001 From: Mihajlo Pavlovic Date: Tue, 23 Jan 2024 13:08:10 +0100 Subject: [PATCH 02/46] Add linearSum contract --- contracts/v1/scoring/linearSum.sol | 134 +++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 contracts/v1/scoring/linearSum.sol diff --git a/contracts/v1/scoring/linearSum.sol b/contracts/v1/scoring/linearSum.sol new file mode 100644 index 00000000..42f202d1 --- /dev/null +++ b/contracts/v1/scoring/linearSum.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HashingProxy} from "../HashingProxy.sol"; +import {ParametersStorage} from "../storage/ParametersStorage.sol"; +import {HubDependent} from "../abstract/HubDependent.sol"; +import {Indexable} from "../interface/Indexable.sol"; +import {Initializable} from "../interface/Initializable.sol"; +import {IScoreFunction} from "../interface/IScoreFunction.sol"; +import {Named} from "../interface/Named.sol"; +import {PRBMathUD60x18} from "@prb/math/contracts/PRBMathUD60x18.sol"; + +// Logarithmic Polynomial Long Division Score Function +contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializable { + using PRBMathUD60x18 for uint256; + + event ParameterChanged(string parameterName, uint256 parameterValue); + + uint8 private constant _ID = 2; + string private constant _NAME = "LinearSum"; + + HashingProxy public hashingProxy; + ParametersStorage public parametersStorage; + + uint256 constant HASH_RING_SIZE = type(uint256).max; + + uint256 public distanceScaleFactor; + + uint32 public w1; + uint32 public w2; + + constructor(address hubAddress) HubDependent(hubAddress) { + distanceScaleFactor = 1000000000000000000; + 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; + } + + // TODO: Implement scoring function + function calculateScore( + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) external view returns (uint40) { + return 1; + } + + // TODO: Change this + function calculateDistance( + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) external view returns (uint256) { + HashingProxy hp = hashingProxy; + bytes32 nodeIdHash = hp.callHashFunction(hashFunctionId, nodeId); + bytes32 keywordHash = hp.callHashFunction(hashFunctionId, keyword); + + return uint256(nodeIdHash ^ keywordHash); + } + + function calculateClockwiseProximityOnHashRing( + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword + ) external view returns (uint256) { + HashingProxy hp = hashingProxy; + bytes32 nodeIdHash = hp.callHashFunction(hashFunctionId, nodeId); + bytes32 keywordHash = hp.callHashFunction(hashFunctionId, keyword); + + uint256 peerPositionOnHashRing = uint256(nodeIdHash); + uint256 keyPositionOnHashRing = uint256(keywordHash); + + uint256 clockwiseDistance; + if (peerPositionOnHashRing > keyPositionOnHashRing) { + uint256 distanceToEnd = HASH_RING_SIZE - peerPositionOnHashRing; + clockwiseDistance = distanceToEnd + keyPositionOnHashRing; + } else { + clockwiseDistance = keyPositionOnHashRing - peerPositionOnHashRing; + } + + return clockwiseDistance; + } + + function calculateBidirectionalProximityOnHashRing( + uint8 hashFunctionId, + bytes calldata peerHash, + bytes calldata keyHash + ) external view returns (uint256) { + uint256 peerPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, peerHash)); + uint256 keyPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyHash)); + + uint256 directDistance; + if (peerPositionOnHashRing > keyPositionOnHashRing) { + directDistance = peerPositionOnHashRing - keyPositionOnHashRing; + } else { + directDistance = keyPositionOnHashRing - peerPositionOnHashRing; + } + + uint256 wraparoundDistance = HASH_RING_SIZE - directDistance; + + return (directDistance < wraparoundDistance) ? directDistance : wraparoundDistance; + } + + function getParameters() external view returns (uint256, uint32, uint32) { + return (distanceScaleFactor, w1, w2); + } + + function setW1(uint32 w1_) external onlyHubOwner { + w1 = w1_; + + emit ParameterChanged("w1", w1); + } + + function setW2(uint32 w2_) external onlyHubOwner { + w2 = w2_; + + emit ParameterChanged("w2", w2); + } + + function calculateScore(uint256 distance, uint96 stake) external view override returns (uint40) {} +} From c6cad84963ec2d0452410014967cdfa7d66d4ecb Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Tue, 23 Jan 2024 13:26:57 +0100 Subject: [PATCH 03/46] Updated sharding table and storage --- contracts/v2/ShardingTable.sol | 73 ++++--------------- contracts/v2/storage/ShardingTableStorage.sol | 8 +- 2 files changed, 19 insertions(+), 62 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index cd264557..af801197 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -7,11 +7,11 @@ import {ShardingTableStorage} 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 "./interface/Named.sol"; -import {Versioned} from "./interface/Versioned.sol"; +import {Named} from "../v1/interface/Named.sol"; +import {Versioned} from "../v1/interface/Versioned.sol"; import {ShardingTableStructs} from "./structs/ShardingTableStructs.sol"; -//import {NULL} from "../v1/constants/ShardingTableConstants.sol"; +import {NULL} from "../v1/constants/ShardingTableConstants.sol"; contract ShardingTable is Named, Versioned, ContractStatus, Initializable { event NodeAdded(uint72 indexed identityId, bytes nodeId, uint96 ask, uint96 stake); @@ -53,68 +53,19 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { return _getShardingTable(sts.head(), sts.nodesCount()); } - // - // function pushBack(uint72 identityId) external onlyContracts { - // ProfileStorage ps = profileStorage; - // require(ps.profileExists(identityId), "Profile doesn't exist"); - // - // ShardingTableStorage sts = shardingTableStorage; - // - // sts.createNodeObject(identityId, NULL, NULL); - // - // if (sts.tail() != NULL) sts.link(sts.tail(), identityId); - // - // sts.setTail(identityId); - // - // if (sts.head() == NULL) sts.setHead(identityId); - // - // sts.incrementNodesCount(); - // - // emit NodeAdded( - // identityId, - // ps.getNodeId(identityId), - // ps.getAsk(identityId), - // stakingStorage.totalStakes(identityId) - // ); - // } - // - // function pushFront(uint72 identityId) external onlyContracts { - // ProfileStorage ps = profileStorage; - // require(ps.profileExists(identityId), "Profile doesn't exist"); - // - // ShardingTableStorage sts = shardingTableStorage; - // - // sts.createNodeObject(identityId, NULL, NULL); - // - // if (sts.head() != NULL) sts.link(identityId, sts.head()); - // - // shardingTableStorage.setHead(identityId); - // - // if (sts.tail() == NULL) sts.setTail(identityId); - // - // sts.incrementNodesCount(); - // - // emit NodeAdded( - // identityId, - // ps.getNodeId(identityId), - // ps.getAsk(identityId), - // stakingStorage.totalStakes(identityId) - // ); - // } - function insertNode(uint72 identityId, uint72 previousIdentityId, uint72 nextIdentityId) external onlyContracts { ProfileStorage ps = profileStorage; require(ps.profileExists(identityId), "Profile doesn't exist"); ShardingTableStorage sts = shardingTableStorage; - ShardingTableStructs.Node previousNode = sts.getNode(previousIdentityId); + ShardingTableStructs.Node memory previousNode = sts.getNode(previousIdentityId); - ShardingTableStructs.Node nextNode = sts.getNode(nextIdentityId); + ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); if ( previousIdentityId == identityId || - (previousIdentityId > identityId && previousNode.index != sts.nodesCount) + (previousIdentityId > identityId && previousNode.index != sts.nodesCount()) ) { revert("Invalid previous node id"); } @@ -129,7 +80,10 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); - for (uint i = nextNode.index + 1; i < sts.nodesCount - 1; i++) { + uint72 count = sts.nodesCount(); + + require(count > 1, "Invalid nodes count"); + for (uint72 i = nextNode.index + 1; i < count - 1; i++) { nextNode.index = i; nextNode = sts.getNode(nextNode.nextIdentityId); } @@ -154,11 +108,14 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); - ShardingTableStructs.Node nextNode = sts.getNode(nodeToRemove.nextIdentityId); + ShardingTableStructs.Node memory nextNode = sts.getNode(nodeToRemove.nextIdentityId); sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); - for (uint i = nodeToRemove.index; i < sts.nodesCount - 1; i++) { + uint72 count = sts.nodesCount(); + + require(count > 1, "Invalid nodes count"); + for (uint72 i = nodeToRemove.index; i < count - 1; i++) { nextNode.index = i; nextNode = sts.getNode(nextNode.nextIdentityId); } diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 81e4625b..e3876532 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.16; -import {HubDependent} from "../abstract/HubDependent.sol"; -import {Named} from "../interface/Named.sol"; -import {Versioned} from "../interface/Versioned.sol"; +import {HubDependent} from "../../v1/abstract/HubDependent.sol"; +import {Named} from "../../v1/interface/Named.sol"; +import {Versioned} from "../../v1/interface/Versioned.sol"; import {ShardingTableStructs} from "../structs/ShardingTableStructs.sol"; -import {NULL} from "../constants/ShardingTableConstants.sol"; +import {NULL} from "../../v1/constants/ShardingTableConstants.sol"; contract ShardingTableStorage is Named, Versioned, HubDependent { string private constant _NAME = "ShardingTableStorage"; From 889b71272f81497d000dc583bcce3dc000790e18 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 13:29:08 +0100 Subject: [PATCH 04/46] Updated deployment scripts to support sharding table rework / new proximity/score functions --- deploy/010_deploy_linear_sum.ts | 37 +++++++++++++++++ ...age.ts => 011_deploy_assertion_storage.ts} | 0 ...2.ts => 012_deploy_identity_storage_v2.ts} | 0 ...rage.ts => 013_deploy_identity_storage.ts} | 0 .../014_deploy_sharding_table_storage_v2.ts | 23 +++++++++++ ...s => 015_deploy_sharding_table_storage.ts} | 10 +++++ ...orage.ts => 016_deploy_staking_storage.ts} | 0 ...orage.ts => 017_deploy_profile_storage.ts} | 0 ...18_deploy_service_agreement_storage_v1.ts} | 0 ..._deploy_service_agreement_storage_v1u1.ts} | 0 ...deploy_service_agreement_storage_proxy.ts} | 0 ...=> 021_deploy_content_asset_storage_v2.ts} | 0 ...ts => 022_deploy_content_asset_storage.ts} | 0 ...> 023_deploy_unfinalized_state_storage.ts} | 0 ...y_assertion.ts => 024_deploy_assertion.ts} | 0 ...loy_identity.ts => 025_deploy_identity.ts} | 0 deploy/026_deploy_sharding_table_v2.ts | 23 +++++++++++ ..._table.ts => 027_deploy_sharding_table.ts} | 10 +++++ deploy/028_deploy_staking_v2.ts | 32 +++++++++++++++ ...eploy_staking.ts => 029_deploy_staking.ts} | 10 +++++ ...eploy_profile.ts => 030_deploy_profile.ts} | 0 ..._v1.ts => 031_deploy_commit_manager_v1.ts} | 0 deploy/032_deploy_commit_manager_v2.ts | 40 +++++++++++++++++++ ....ts => 033_deploy_commit_manager_v1_u1.ts} | 10 +++++ ...r_v1.ts => 034_deploy_proof_manager_v1.ts} | 0 ...1.ts => 035_deploy_proof_manager_v1_u1.ts} | 0 ....ts => 036_deploy_service_agreement_v1.ts} | 0 ...t_asset.ts => 037_deploy_content_asset.ts} | 0 28 files changed, 195 insertions(+) create mode 100644 deploy/010_deploy_linear_sum.ts rename deploy/{010_deploy_assertion_storage.ts => 011_deploy_assertion_storage.ts} (100%) rename deploy/{011_deploy_identity_storage_v2.ts => 012_deploy_identity_storage_v2.ts} (100%) rename deploy/{012_deploy_identity_storage.ts => 013_deploy_identity_storage.ts} (100%) create mode 100644 deploy/014_deploy_sharding_table_storage_v2.ts rename deploy/{013_deploy_sharding_table_storage.ts => 015_deploy_sharding_table_storage.ts} (52%) rename deploy/{014_deploy_staking_storage.ts => 016_deploy_staking_storage.ts} (100%) rename deploy/{015_deploy_profile_storage.ts => 017_deploy_profile_storage.ts} (100%) rename deploy/{016_deploy_service_agreement_storage_v1.ts => 018_deploy_service_agreement_storage_v1.ts} (100%) rename deploy/{017_deploy_service_agreement_storage_v1u1.ts => 019_deploy_service_agreement_storage_v1u1.ts} (100%) rename deploy/{018_deploy_service_agreement_storage_proxy.ts => 020_deploy_service_agreement_storage_proxy.ts} (100%) rename deploy/{019_deploy_content_asset_storage_v2.ts => 021_deploy_content_asset_storage_v2.ts} (100%) rename deploy/{020_deploy_content_asset_storage.ts => 022_deploy_content_asset_storage.ts} (100%) rename deploy/{021_deploy_unfinalized_state_storage.ts => 023_deploy_unfinalized_state_storage.ts} (100%) rename deploy/{022_deploy_assertion.ts => 024_deploy_assertion.ts} (100%) rename deploy/{023_deploy_identity.ts => 025_deploy_identity.ts} (100%) create mode 100644 deploy/026_deploy_sharding_table_v2.ts rename deploy/{024_deploy_sharding_table.ts => 027_deploy_sharding_table.ts} (57%) create mode 100644 deploy/028_deploy_staking_v2.ts rename deploy/{025_deploy_staking.ts => 029_deploy_staking.ts} (64%) rename deploy/{026_deploy_profile.ts => 030_deploy_profile.ts} (100%) rename deploy/{027_deploy_commit_manager_v1.ts => 031_deploy_commit_manager_v1.ts} (100%) create mode 100644 deploy/032_deploy_commit_manager_v2.ts rename deploy/{029_deploy_commit_manager_v1_u1.ts => 033_deploy_commit_manager_v1_u1.ts} (70%) rename deploy/{028_deploy_proof_manager_v1.ts => 034_deploy_proof_manager_v1.ts} (100%) rename deploy/{030_deploy_proof_manager_v1_u1.ts => 035_deploy_proof_manager_v1_u1.ts} (100%) rename deploy/{031_deploy_service_agreement_v1.ts => 036_deploy_service_agreement_v1.ts} (100%) rename deploy/{032_deploy_content_asset.ts => 037_deploy_content_asset.ts} (100%) 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/013_deploy_sharding_table_storage.ts b/deploy/015_deploy_sharding_table_storage.ts similarity index 52% rename from deploy/013_deploy_sharding_table_storage.ts rename to deploy/015_deploy_sharding_table_storage.ts index 489a7155..3f927fa8 100644 --- a/deploy/013_deploy_sharding_table_storage.ts +++ b/deploy/015_deploy_sharding_table_storage.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('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', }); 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/015_deploy_profile_storage.ts b/deploy/017_deploy_profile_storage.ts similarity index 100% rename from deploy/015_deploy_profile_storage.ts rename to deploy/017_deploy_profile_storage.ts diff --git a/deploy/016_deploy_service_agreement_storage_v1.ts b/deploy/018_deploy_service_agreement_storage_v1.ts similarity index 100% rename from deploy/016_deploy_service_agreement_storage_v1.ts rename to deploy/018_deploy_service_agreement_storage_v1.ts diff --git a/deploy/017_deploy_service_agreement_storage_v1u1.ts b/deploy/019_deploy_service_agreement_storage_v1u1.ts similarity index 100% rename from deploy/017_deploy_service_agreement_storage_v1u1.ts rename to deploy/019_deploy_service_agreement_storage_v1u1.ts diff --git a/deploy/018_deploy_service_agreement_storage_proxy.ts b/deploy/020_deploy_service_agreement_storage_proxy.ts similarity index 100% rename from deploy/018_deploy_service_agreement_storage_proxy.ts rename to deploy/020_deploy_service_agreement_storage_proxy.ts diff --git a/deploy/019_deploy_content_asset_storage_v2.ts b/deploy/021_deploy_content_asset_storage_v2.ts similarity index 100% rename from deploy/019_deploy_content_asset_storage_v2.ts rename to deploy/021_deploy_content_asset_storage_v2.ts diff --git a/deploy/020_deploy_content_asset_storage.ts b/deploy/022_deploy_content_asset_storage.ts similarity index 100% rename from deploy/020_deploy_content_asset_storage.ts rename to deploy/022_deploy_content_asset_storage.ts diff --git a/deploy/021_deploy_unfinalized_state_storage.ts b/deploy/023_deploy_unfinalized_state_storage.ts similarity index 100% rename from deploy/021_deploy_unfinalized_state_storage.ts rename to deploy/023_deploy_unfinalized_state_storage.ts diff --git a/deploy/022_deploy_assertion.ts b/deploy/024_deploy_assertion.ts similarity index 100% rename from deploy/022_deploy_assertion.ts rename to deploy/024_deploy_assertion.ts diff --git a/deploy/023_deploy_identity.ts b/deploy/025_deploy_identity.ts similarity index 100% rename from deploy/023_deploy_identity.ts rename to deploy/025_deploy_identity.ts diff --git a/deploy/026_deploy_sharding_table_v2.ts b/deploy/026_deploy_sharding_table_v2.ts new file mode 100644 index 00000000..c57005f8 --- /dev/null +++ b/deploy/026_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/027_deploy_sharding_table.ts similarity index 57% rename from deploy/024_deploy_sharding_table.ts rename to deploy/027_deploy_sharding_table.ts index 5f957641..9650f41b 100644 --- a/deploy/024_deploy_sharding_table.ts +++ b/deploy/027_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/028_deploy_staking_v2.ts b/deploy/028_deploy_staking_v2.ts new file mode 100644 index 00000000..fb83dc4e --- /dev/null +++ b/deploy/028_deploy_staking_v2.ts @@ -0,0 +1,32 @@ +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', +]; diff --git a/deploy/025_deploy_staking.ts b/deploy/029_deploy_staking.ts similarity index 64% rename from deploy/025_deploy_staking.ts rename to deploy/029_deploy_staking.ts index 0b73e287..16c836d3 100644 --- a/deploy/025_deploy_staking.ts +++ b/deploy/029_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/030_deploy_profile.ts similarity index 100% rename from deploy/026_deploy_profile.ts rename to deploy/030_deploy_profile.ts diff --git a/deploy/027_deploy_commit_manager_v1.ts b/deploy/031_deploy_commit_manager_v1.ts similarity index 100% rename from deploy/027_deploy_commit_manager_v1.ts rename to deploy/031_deploy_commit_manager_v1.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..a159d18c --- /dev/null +++ b/deploy/032_deploy_commit_manager_v2.ts @@ -0,0 +1,40 @@ +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 V2...'); + + const CommitManagerV1U1 = await hre.helpers.deploy({ + newContractName: 'CommitManagerV2', + newContractNameInHub: 'CommitManagerV1U1', + }); + + await hre.helpers.updateContractParameters('CommitManagerV1U1', CommitManagerV1U1); +}; + +export default func; +func.tags = ['CommitManagerV2', 'v2']; +func.dependencies = [ + 'ContentAssetStorage', + 'HubV2', + 'IdentityStorageV2', + 'ScoringProxy', + 'Log2PLDSF', + 'ParametersStorage', + 'ProfileStorage', + 'ServiceAgreementStorageProxy', + 'HashingProxy', + 'SHA256', + 'ShardingTableStorageV2', + 'StakingV2', + 'StakingStorageV2', + 'UnfinalizedStateStorage', +]; diff --git a/deploy/029_deploy_commit_manager_v1_u1.ts b/deploy/033_deploy_commit_manager_v1_u1.ts similarity index 70% rename from deploy/029_deploy_commit_manager_v1_u1.ts rename to deploy/033_deploy_commit_manager_v1_u1.ts index a99b3a8c..a5c4e8af 100644 --- a/deploy/029_deploy_commit_manager_v1_u1.ts +++ b/deploy/033_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', }); diff --git a/deploy/028_deploy_proof_manager_v1.ts b/deploy/034_deploy_proof_manager_v1.ts similarity index 100% rename from deploy/028_deploy_proof_manager_v1.ts rename to deploy/034_deploy_proof_manager_v1.ts diff --git a/deploy/030_deploy_proof_manager_v1_u1.ts b/deploy/035_deploy_proof_manager_v1_u1.ts similarity index 100% rename from deploy/030_deploy_proof_manager_v1_u1.ts rename to deploy/035_deploy_proof_manager_v1_u1.ts diff --git a/deploy/031_deploy_service_agreement_v1.ts b/deploy/036_deploy_service_agreement_v1.ts similarity index 100% rename from deploy/031_deploy_service_agreement_v1.ts rename to deploy/036_deploy_service_agreement_v1.ts diff --git a/deploy/032_deploy_content_asset.ts b/deploy/037_deploy_content_asset.ts similarity index 100% rename from deploy/032_deploy_content_asset.ts rename to deploy/037_deploy_content_asset.ts From c66d78b6b76ec0f9fdd1e60e789b9c4c62a958af Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 13:55:15 +0100 Subject: [PATCH 05/46] Bumped version of the package to the 4.2.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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..274eee74 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": [ From 1b8e9784e0c7dc65fe4ac3f674df4a3d3e4db650 Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Tue, 23 Jan 2024 14:27:35 +0100 Subject: [PATCH 06/46] Added handling when sharding table is empty --- contracts/v2/ShardingTable.sol | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index af801197..bb553af8 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -63,33 +63,31 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); - if ( + if (sts.nodesCount() == 0) { + sts.createNodeObject(identityId, 0, NULL, NULL); + } else if ( previousIdentityId == identityId || (previousIdentityId > identityId && previousNode.index != sts.nodesCount()) ) { revert("Invalid previous node id"); - } - - if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { + } else if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { revert("Invalid next node id"); - } - - if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { + } else if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { revert("Invalid previous and next node id"); - } + } else { + sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); - sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); + uint72 count = sts.nodesCount(); - uint72 count = sts.nodesCount(); + require(count > 1, "Invalid nodes count"); + for (uint72 i = nextNode.index + 1; i < count - 1; i++) { + nextNode.index = i; + nextNode = sts.getNode(nextNode.nextIdentityId); + } - require(count > 1, "Invalid nodes count"); - for (uint72 i = nextNode.index + 1; i < count - 1; i++) { - nextNode.index = i; - nextNode = sts.getNode(nextNode.nextIdentityId); + sts.link(previousIdentityId, identityId, nextIdentityId); } - sts.link(previousIdentityId, identityId, nextIdentityId); - sts.incrementNodesCount(); emit NodeAdded( From 62ec545bea9f5bf3de366aada627cf222f51456a Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Tue, 23 Jan 2024 14:30:59 +0100 Subject: [PATCH 07/46] Reverted changes --- contracts/v2/ShardingTable.sol | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index bb553af8..af801197 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -63,31 +63,33 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); - if (sts.nodesCount() == 0) { - sts.createNodeObject(identityId, 0, NULL, NULL); - } else if ( + if ( previousIdentityId == identityId || (previousIdentityId > identityId && previousNode.index != sts.nodesCount()) ) { revert("Invalid previous node id"); - } else if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { + } + + if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { revert("Invalid next node id"); - } else if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { + } + + if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { revert("Invalid previous and next node id"); - } else { - sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); + } - uint72 count = sts.nodesCount(); + sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); - require(count > 1, "Invalid nodes count"); - for (uint72 i = nextNode.index + 1; i < count - 1; i++) { - nextNode.index = i; - nextNode = sts.getNode(nextNode.nextIdentityId); - } + uint72 count = sts.nodesCount(); - sts.link(previousIdentityId, identityId, nextIdentityId); + require(count > 1, "Invalid nodes count"); + for (uint72 i = nextNode.index + 1; i < count - 1; i++) { + nextNode.index = i; + nextNode = sts.getNode(nextNode.nextIdentityId); } + sts.link(previousIdentityId, identityId, nextIdentityId); + sts.incrementNodesCount(); emit NodeAdded( From 9bdfca90ab84ed42b2b18ed650aaa40d0ee15ed0 Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Tue, 23 Jan 2024 15:09:43 +0100 Subject: [PATCH 08/46] Added handling when sharding table is empty --- contracts/v2/ShardingTable.sol | 47 ++++++++++++------- contracts/v2/storage/ShardingTableStorage.sol | 10 ++-- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index af801197..79021ca0 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -63,32 +63,45 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); - if ( + if (sts.nodesCount() == 0) { + sts.createNodeObject(identityId, 0, NULL, NULL); + sts.setHead(identityId); + } else if (sts.nodesCount() == 1) { + ShardingTableStructs.Node memory head = sts.getNode(sts.head()); + if (head.identityId < identityId) { + sts.createNodeObject(identityId, 1, head.identityId, head.identityId); + } else { + sts.createNodeObject(identityId, 0, head.identityId, head.identityId); + sts.setHead(identityId); + } + sts.setNextIdentityId(head.identityId, identityId); + sts.setPrevIdentityId(head.identityId, identityId); + } else if ( previousIdentityId == identityId || (previousIdentityId > identityId && previousNode.index != sts.nodesCount()) ) { revert("Invalid previous node id"); - } - - if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { + } else if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { revert("Invalid next node id"); - } - - if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { + } else if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { revert("Invalid previous and next node id"); - } + } else { + sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); - sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); + if (nextNode.index == 0) { + sts.setHead(identityId); + } - uint72 count = sts.nodesCount(); + uint72 count = sts.nodesCount(); - require(count > 1, "Invalid nodes count"); - for (uint72 i = nextNode.index + 1; i < count - 1; i++) { - nextNode.index = i; - nextNode = sts.getNode(nextNode.nextIdentityId); - } + require(count > 1, "Invalid nodes count"); + for (uint72 i = nextNode.index + 1; i < count - 1; i++) { + sts.setIndex(nextNode.identityId, i); + nextNode = sts.getNode(nextNode.nextIdentityId); + } - sts.link(previousIdentityId, identityId, nextIdentityId); + sts.link(previousIdentityId, identityId, nextIdentityId); + } sts.incrementNodesCount(); @@ -116,7 +129,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { require(count > 1, "Invalid nodes count"); for (uint72 i = nodeToRemove.index; i < count - 1; i++) { - nextNode.index = i; + sts.setIndex(nextNode.identityId, i); nextNode = sts.getNode(nextNode.nextIdentityId); } diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index e3876532..d62085a2 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -13,7 +13,6 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { string private constant _VERSION = "2.0.0"; uint72 public head; - uint72 public tail; uint72 public nodesCount; // identityId => Node @@ -21,7 +20,6 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { constructor(address hubAddress) HubDependent(hubAddress) { head = NULL; - tail = NULL; } function name() external pure virtual override returns (string memory) { @@ -44,10 +42,6 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { head = identityId; } - function setTail(uint72 identityId) external onlyContracts { - tail = identityId; - } - function createNodeObject( uint72 identityId, uint72 prevIdentityId, @@ -82,6 +76,10 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { nodes[identityId].nextIdentityId = newNextIdentityId; } + function setIndex(uint72 identityId, uint72 index) external onlyContracts { + nodes[identityId].index = index; + } + function getMultipleNodes( uint72 firstIdentityId, uint16 nodesNumber From 7907056a03309b8f195d98b4cfb258c5b71a4f43 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 15:23:08 +0100 Subject: [PATCH 09/46] Added missing parameters for ShardingTable, reworked insertion of the new node to the linked list --- contracts/v2/ShardingTable.sol | 88 +++++++++++++------ contracts/v2/errors/ShardingTableErrors.sol | 25 ++++++ contracts/v2/storage/ShardingTableStorage.sol | 19 ++-- contracts/v2/structs/ShardingTableStructs.sol | 3 +- 4 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 contracts/v2/errors/ShardingTableErrors.sol diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index af801197..c5bc319e 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -3,13 +3,14 @@ pragma solidity ^0.8.16; import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; -import {ShardingTableStorage} from "./storage/ShardingTableStorage.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 {ShardingTableStructs} from "./structs/ShardingTableStructs.sol"; +import {ShardingTableErrors} from "./errors/ShardingTableErrors.sol"; import {NULL} from "../v1/constants/ShardingTableConstants.sol"; @@ -18,10 +19,10 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { event NodeRemoved(uint72 indexed identityId, bytes nodeId); string private constant _NAME = "ShardingTable"; - string private constant _VERSION = "1.0.1"; + string private constant _VERSION = "2.0.0"; ProfileStorage public profileStorage; - ShardingTableStorage public shardingTableStorage; + ShardingTableStorageV2 public shardingTableStorage; StakingStorage public stakingStorage; // solhint-disable-next-line no-empty-blocks @@ -29,7 +30,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); - shardingTableStorage = ShardingTableStorage(hub.getContractAddress("ShardingTableStorage")); + shardingTableStorage = ShardingTableStorageV2(hub.getContractAddress("ShardingTableStorage")); stakingStorage = StakingStorage(hub.getContractAddress("StakingStorage")); } @@ -49,48 +50,77 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { } function getShardingTable() external view returns (ShardingTableStructs.NodeInfo[] memory) { - ShardingTableStorage sts = shardingTableStorage; + ShardingTableStorageV2 sts = shardingTableStorage; return _getShardingTable(sts.head(), sts.nodesCount()); } - function insertNode(uint72 identityId, uint72 previousIdentityId, uint72 nextIdentityId) external onlyContracts { + function insertNode(uint72 identityId, uint72 prevIdentityId, uint72 nextIdentityId) external onlyContracts { ProfileStorage ps = profileStorage; - require(ps.profileExists(identityId), "Profile doesn't exist"); - ShardingTableStorage sts = shardingTableStorage; + uint256 newNodeHashRingPosition = uint256(ps.getNodeAddress(identityId, 1)); - ShardingTableStructs.Node memory previousNode = sts.getNode(previousIdentityId); + require(newNodeHashRingPosition != 0, "Profile doesn't exist"); - ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); + ShardingTableStorageV2 sts = shardingTableStorage; - if ( - previousIdentityId == identityId || - (previousIdentityId > identityId && previousNode.index != sts.nodesCount()) - ) { - revert("Invalid previous node id"); + ShardingTableStructs.Node memory prevNode = sts.getNode(prevIdentityId); + + if (prevNode.hashRingPosition > newNodeHashRingPosition) { + revert ShardingTableErrors.InvalidPreviousIdentityId( + identityId, + newNodeHashRingPosition, + prevIdentityId, + prevNode.hashRingPosition + ); } - if (nextIdentityId == identityId || (nextIdentityId < identityId && nextNode.index != 0)) { - revert("Invalid next node id"); + ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); + + if (nextNode.identityId != NULL && nextNode.hashRingPosition < newNodeHashRingPosition) { + revert ShardingTableErrors.InvalidNextIdentityId( + identityId, + newNodeHashRingPosition, + nextIdentityId, + nextNode.hashRingPosition + ); } - if (nextNode.index - previousNode.index != 1 && nextNode.index != 0) { - revert("Invalid previous and next node id"); + if (prevNode.nextIdentityId != nextNode.prevIdentityId) { + revert ShardingTableErrors.InvalidPreviousOrNextIdentityId( + identityId, + prevIdentityId, + nextNode.prevIdentityId, + nextIdentityId, + prevNode.nextIdentityId + ); } - sts.createNodeObject(identityId, nextNode.index, previousIdentityId, nextIdentityId); + sts.createNodeObject( + uint256(ps.getNodeAddress(identityId, 1)), + nextNode.index, + identityId, + prevIdentityId, + nextIdentityId + ); - uint72 count = sts.nodesCount(); + sts.incrementNodesCount(); - require(count > 1, "Invalid nodes count"); - for (uint72 i = nextNode.index + 1; i < count - 1; i++) { - nextNode.index = i; - nextNode = sts.getNode(nextNode.nextIdentityId); + if (prevIdentityId == NULL) { + sts.setHead(identityId); + } else { + sts.link(prevIdentityId, identityId); } - sts.link(previousIdentityId, identityId, nextIdentityId); + if (nextIdentityId == NULL) { + sts.setTail(identityId); + } else { + sts.link(identityId, nextIdentityId); + } - sts.incrementNodesCount(); + while (nextIdentityId != NULL) { + sts.incrementNodeIndex(nextIdentityId); + nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + } emit NodeAdded( identityId, @@ -104,7 +134,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ProfileStorage ps = profileStorage; require(ps.profileExists(identityId), "Profile doesn't exist"); - ShardingTableStorage sts = shardingTableStorage; + ShardingTableStorageV2 sts = shardingTableStorage; ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); @@ -131,7 +161,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { uint72 nodesNumber ) internal view virtual returns (ShardingTableStructs.NodeInfo[] memory) { ShardingTableStructs.NodeInfo[] memory nodesPage; - ShardingTableStorage sts = shardingTableStorage; + ShardingTableStorageV2 sts = shardingTableStorage; if ((sts.nodesCount() == 0) || (nodesNumber == 0)) { return nodesPage; diff --git a/contracts/v2/errors/ShardingTableErrors.sol b/contracts/v2/errors/ShardingTableErrors.sol new file mode 100644 index 00000000..ff55b50b --- /dev/null +++ b/contracts/v2/errors/ShardingTableErrors.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ShardingTableErrors { + error InvalidPreviousIdentityId( + uint72 identityId, + uint256 hashRingPosition, + uint72 prevIdentityId, + uint256 prevHashRingPosition + ); + error InvalidNextIdentityId( + uint72 identityId, + uint256 hashRingPosition, + uint72 nextIdentityId, + uint256 nextHashRingPosition + ); + error InvalidPreviousOrNextIdentityId( + uint72 identityId, + uint72 sentPrevIdentityId, + uint72 realPrevIdentityId, + uint72 sentNextIdentityId, + uint72 realNextIdentityId + ); +} diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index e3876532..245d5c99 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -8,7 +8,7 @@ import {Versioned} from "../../v1/interface/Versioned.sol"; import {ShardingTableStructs} from "../structs/ShardingTableStructs.sol"; import {NULL} from "../../v1/constants/ShardingTableConstants.sol"; -contract ShardingTableStorage is Named, Versioned, HubDependent { +contract ShardingTableStorageV2 is Named, Versioned, HubDependent { string private constant _NAME = "ShardingTableStorage"; string private constant _VERSION = "2.0.0"; @@ -49,14 +49,16 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { } function createNodeObject( + uint256 hashRingPosition, uint72 identityId, uint72 prevIdentityId, uint72 nextIdentityId, uint72 index ) external onlyContracts { nodes[identityId] = ShardingTableStructs.Node({ - identityId: identityId, + hashRingPosition: hashRingPosition, index: index, + identityId: identityId, prevIdentityId: prevIdentityId, nextIdentityId: nextIdentityId }); @@ -82,6 +84,14 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { nodes[identityId].nextIdentityId = newNextIdentityId; } + function incrementNodeIndex(uint72 identityId) external onlyContracts { + nodes[identityId].index += 1; + } + + function decrementNodeIndex(uint72 identityId) external onlyContracts { + nodes[identityId].index -= 1; + } + function getMultipleNodes( uint72 firstIdentityId, uint16 nodesNumber @@ -100,11 +110,6 @@ contract ShardingTableStorage is Named, Versioned, HubDependent { return nodesPage; } - function link(uint72 leftNodeIdentityId, uint72 newIdentityId, uint72 rightNodeIdentityId) external onlyContracts { - nodes[leftNodeIdentityId].nextIdentityId = newIdentityId; - nodes[rightNodeIdentityId].prevIdentityId = newIdentityId; - } - function link(uint72 leftNodeIdentityId, uint72 rightNodeIdentityId) external onlyContracts { nodes[leftNodeIdentityId].nextIdentityId = rightNodeIdentityId; nodes[rightNodeIdentityId].prevIdentityId = leftNodeIdentityId; diff --git a/contracts/v2/structs/ShardingTableStructs.sol b/contracts/v2/structs/ShardingTableStructs.sol index f7f15554..20845946 100644 --- a/contracts/v2/structs/ShardingTableStructs.sol +++ b/contracts/v2/structs/ShardingTableStructs.sol @@ -11,8 +11,9 @@ library ShardingTableStructs { } struct Node { - uint72 identityId; + uint256 hashRingPosition; uint72 index; + uint72 identityId; uint72 prevIdentityId; uint72 nextIdentityId; } From 74c4d792da86c923e9145b731c7f0b5a6f779dfe Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 15:32:02 +0100 Subject: [PATCH 10/46] Removed tail setter for sharding table --- contracts/v2/ShardingTable.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index da5a1cad..867b81cb 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -111,9 +111,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { sts.link(prevIdentityId, identityId); } - if (nextIdentityId == NULL) { - sts.setTail(identityId); - } else { + if (nextIdentityId != NULL) { sts.link(identityId, nextIdentityId); } From 0e22758d341f7a989bc4235a195b9b8c44bbd744 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 15:37:00 +0100 Subject: [PATCH 11/46] Updated node removal from sharding table v2 --- contracts/v2/ShardingTable.sol | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index 867b81cb..d989b904 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -102,7 +102,6 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { prevIdentityId, nextIdentityId ); - sts.incrementNodesCount(); if (prevIdentityId == NULL) { @@ -136,16 +135,12 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); - ShardingTableStructs.Node memory nextNode = sts.getNode(nodeToRemove.nextIdentityId); - sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); - uint72 count = sts.nodesCount(); - - require(count > 1, "Invalid nodes count"); - for (uint72 i = nodeToRemove.index; i < count - 1; i++) { - sts.setIndex(nextNode.identityId, i); - nextNode = sts.getNode(nextNode.nextIdentityId); + uint72 nextIdentityId = nodeToRemove.nextIdentityId; + while (nextIdentityId != NULL) { + sts.decrementNodeIndex(nextIdentityId); + nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; } sts.deleteNodeObject(identityId); From 03176b863d39410d71609f3e3e355421fe40d592 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 23 Jan 2024 15:38:50 +0100 Subject: [PATCH 12/46] Removed redundant checks from 'removeNode' function --- contracts/v2/ShardingTable.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index d989b904..5e0dbc93 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -128,13 +128,12 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { } function removeNode(uint72 identityId) external onlyContracts { - ProfileStorage ps = profileStorage; - require(ps.profileExists(identityId), "Profile doesn't exist"); - ShardingTableStorageV2 sts = shardingTableStorage; ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); + require(nodeToRemove.identityId != 0, "Profile doesn't exist"); + sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); uint72 nextIdentityId = nodeToRemove.nextIdentityId; @@ -146,7 +145,7 @@ contract ShardingTable is Named, Versioned, ContractStatus, Initializable { sts.deleteNodeObject(identityId); sts.decrementNodesCount(); - emit NodeRemoved(identityId, ps.getNodeId(identityId)); + emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); } function _getShardingTable( From 06dde1f15148bb14a953fba8595e2a3288529e6e Mon Sep 17 00:00:00 2001 From: Mihajlo Pavlovic Date: Wed, 24 Jan 2024 09:39:49 +0100 Subject: [PATCH 13/46] New commit manager --- contracts/v1/CommitManagerV1New.sol | 424 ++++++++++++++++++ contracts/v1/ScoringProxy.sol | 11 +- contracts/v1/errors/CommitManagerErrorsV1.sol | 24 + .../v1/errors/ServiceAgreementErrorsV1New.sol | 56 +++ .../v1/structs/ServiceAgreementStructsV1.sol | 3 + 5 files changed, 512 insertions(+), 6 deletions(-) create mode 100644 contracts/v1/CommitManagerV1New.sol create mode 100644 contracts/v1/errors/CommitManagerErrorsV1.sol create mode 100644 contracts/v1/errors/ServiceAgreementErrorsV1New.sol diff --git a/contracts/v1/CommitManagerV1New.sol b/contracts/v1/CommitManagerV1New.sol new file mode 100644 index 00000000..88e3ead7 --- /dev/null +++ b/contracts/v1/CommitManagerV1New.sol @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HashingProxy} from "./HashingProxy.sol"; +import {ScoringProxy} from "./ScoringProxy.sol"; +import {Staking} from "./Staking.sol"; +import {IdentityStorage} from "./storage/IdentityStorage.sol"; +import {ParametersStorage} from "./storage/ParametersStorage.sol"; +import {ProfileStorage} from "./storage/ProfileStorage.sol"; +import {ServiceAgreementStorageProxy} from "./storage/ServiceAgreementStorageProxy.sol"; +import {ShardingTableStorageV2} from "../v2/storage/ShardingTableStorage.sol"; +import {StakingStorage} from "./storage/StakingStorage.sol"; +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 {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; +import {GeneralErrors} from "./errors/GeneralErrors.sol"; +import {ServiceAgreementErrorsV1} from "./errors/ServiceAgreementErrorsV1.sol"; +import {ServiceAgreementErrorsV1New} from "./errors/ServiceAgreementErrorsV1New.sol"; +import {ServiceAgreementStructsV1} from "./structs/ServiceAgreementStructsV1.sol"; +import {ShardingTableStructs} from "../v2/structs/ShardingTableStructs.sol"; +import {CommitManagerErrorsV1} from "./errors/CommitManagerErrorsV1.sol"; + +contract CommitManagerV1New 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-new"; + string private constant _VERSION = "1.0.1"; + + bool[4] public reqs = [false, false, false, false]; + + HashingProxy public hashingProxy; + ScoringProxy public scoringProxy; + Staking public stakingContract; + IdentityStorage 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 { + hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); + scoringProxy = ScoringProxy(hub.getContractAddress("ScoringProxy")); + stakingContract = Staking(hub.getContractAddress("Staking")); + identityStorage = IdentityStorage(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 = hashingProxy.callHashFunction( + args.hashFunctionId, + abi.encodePacked(args.assetContract, args.tokenId, args.keyword) + ); + + if (!sasProxy.agreementV1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + 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) + ); + } + + uint8 agreementScoreFunctionId = sasProxy.getAgreementScoreFunctionId(agreementId); + + if (agreementScoreFunctionId != 2) { + revert ServiceAgreementErrorsV1New.WrongScoreFunctionId( + agreementId, + args.epoch, + agreementScoreFunctionId, + 2, + block.timestamp + ); + } + + if (!shardingTableStorage.nodeExists(args.closestNode)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + args.closestNode, + ps.getNodeId(args.closestNode), + ps.getAsk(args.closestNode), + stakingStorage.totalStakes(args.closestNode) + ); + } + + if (!shardingTableStorage.nodeExists(args.leftNeighbourHoodEdge)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + args.leftNeighbourHoodEdge, + ps.getNodeId(args.leftNeighbourHoodEdge), + ps.getAsk(args.leftNeighbourHoodEdge), + stakingStorage.totalStakes(args.leftNeighbourHoodEdge) + ); + } + + if (!shardingTableStorage.nodeExists(args.rightNeighbourHoodEdge)) { + ProfileStorage ps = profileStorage; + + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( + args.rightNeighbourHoodEdge, + ps.getNodeId(args.rightNeighbourHoodEdge), + ps.getAsk(args.rightNeighbourHoodEdge), + stakingStorage.totalStakes(args.rightNeighbourHoodEdge) + ); + } + + ShardingTableStructs.Node memory closestNode = shardingTableStorage.getNode(args.closestNode); + ShardingTableStructs.Node memory leftNeighbourhoodEdge = shardingTableStorage.getNode( + args.leftNeighbourHoodEdge + ); + ShardingTableStructs.Node memory rightNeighbourhoodEdge = shardingTableStorage.getNode( + args.rightNeighbourHoodEdge + ); + + bool isBetween = (leftNeighbourhoodEdge.index > rightNeighbourhoodEdge.index) + ? ((closestNode.index > leftNeighbourhoodEdge.index) || (closestNode.index < rightNeighbourhoodEdge.index)) + : ((closestNode.index > leftNeighbourhoodEdge.index) && (closestNode.index < rightNeighbourhoodEdge.index)); + + if (!isBetween) { + revert CommitManagerErrorsV1.closestNodeNotInNeighbourhood( + agreementId, + args.leftNeighbourHoodEdge, + args.rightNeighbourHoodEdge, + args.closestNode, + args.epoch, + block.timestamp + ); + } + + uint256 numberOfNodes = shardingTableStorage.nodesCount(); + uint256 clockwiseDistance = (rightNeighbourhoodEdge.index + numberOfNodes - leftNeighbourhoodEdge.index) % + numberOfNodes; + uint256 counterclockwiseDistance = (leftNeighbourhoodEdge.index + + numberOfNodes - + rightNeighbourhoodEdge.index) % numberOfNodes; + + uint256 indexDistance = (clockwiseDistance < counterclockwiseDistance) + ? clockwiseDistance + : counterclockwiseDistance; + + //distance between 20 nodes is 19 (this shold be constant) + if (!(indexDistance == 19)) { + revert CommitManagerErrorsV1.negihbourhoodWrongSize( + agreementId, + args.leftNeighbourHoodEdge, + args.rightNeighbourHoodEdge, + numberOfNodes, + 20, + indexDistance, + args.epoch, + block.timestamp + ); + } + + uint256 hashRingNeighbourhoodDistance = calculateHashRingDistance( + leftNeighbourhoodEdge.hashRingPosition, + rightNeighbourhoodEdge.hashRingPosition + ); + + bytes32 keywordHash = hashingProxy.callHashFunction(args.hashFunctionId, args.keyword); + bytes32 nodeIdHash = hashingProxy.callHashFunction(args.hashFunctionId, profileStorage.getNodeId(identityId)); + + uint256 distance = calculateHashRingDistance( + leftNeighbourhoodEdge.hashRingPosition, + rightNeighbourhoodEdge.hashRingPosition + ); + + // uint40 score = scoringProxy.callScoreFunction( + // mappedDistance, + // mappedStake + // ); + + _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 _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); + } else { + // [] <-> [H] <-> [RC] <-> [] + // [] <-> [H] <-> [RC] <-(NL)-> [NC] <-> [] + _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 + ); + } + + function calculateHashRingDistance( + uint leftNodePositionOnHashRing, + uint rightNodePositionOnHashRing + ) private view returns (uint256) { + uint256 directDistance = (leftNodePositionOnHashRing >= rightNodePositionOnHashRing) + ? (leftNodePositionOnHashRing - rightNodePositionOnHashRing) + : (rightNodePositionOnHashRing - leftNodePositionOnHashRing); + + uint256 reverseDistance = HASH_RING_SIZE - directDistance; + + return (directDistance < reverseDistance) ? directDistance : reverseDistance; + } +} diff --git a/contracts/v1/ScoringProxy.sol b/contracts/v1/ScoringProxy.sol index 83b0ddd5..a20e29e3 100644 --- a/contracts/v1/ScoringProxy.sol +++ b/contracts/v1/ScoringProxy.sol @@ -44,16 +44,15 @@ contract ScoringProxy is Named, Versioned, ContractStatus { scoreFunctionSet.remove(scoreFunctionId); } + //remove everthring except id, noramlised distance, normalized stake function callScoreFunction( uint8 scoreFunctionId, - uint8 hashFunctionId, - bytes calldata nodeId, - bytes calldata keyword, - uint96 stake + uint256 mappedDistance, + uint256 mappedStake ) external view returns (uint40) { IScoreFunction scoringFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); - uint256 distance = scoringFunction.calculateDistance(hashFunctionId, nodeId, keyword); - return scoringFunction.calculateScore(distance, stake); + + return scoringFunction.calculateScore(mappedDistance, mappedStake); } function getScoreFunctionName(uint8 scoreFunctionId) external view returns (string memory) { diff --git a/contracts/v1/errors/CommitManagerErrorsV1.sol b/contracts/v1/errors/CommitManagerErrorsV1.sol new file mode 100644 index 00000000..8242df6f --- /dev/null +++ b/contracts/v1/errors/CommitManagerErrorsV1.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library CommitManagerErrorsV1 { + error closestNodeNotInNeighbourhood( + bytes32 agreementId, + uint72 leftNeighbourhoodEdge, + uint72 rightNeighbourhoodEdge, + uint72 closestNode, + uint16 epoch, + uint256 timeNow + ); + error negihbourhoodWrongSize( + bytes32 agreementId, + uint72 leftNeighbourhoodEdge, + uint72 rightNeighbourhoodEdge, + uint256 numberOfNodes, + uint256 negihbourhoodExpectedSize, + uint256 negihbourhoodActualSize, + uint16 epoch, + uint256 timeNow + ); +} diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1New.sol b/contracts/v1/errors/ServiceAgreementErrorsV1New.sol new file mode 100644 index 00000000..1ac8ea15 --- /dev/null +++ b/contracts/v1/errors/ServiceAgreementErrorsV1New.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ServiceAgreementErrorsV1New { + error ServiceAgreementDoesntExist(bytes32 agreementId); + error EmptyAssetCreatorAddress(); + error AssetStorageNotInTheHub(address contractAddress); + error EmptyKeyword(); + error ZeroEpochsNumber(); + error ZeroTokenAmount(); + error ScoreFunctionDoesntExist(uint8 scoreFunctionId); + error TooLowAllowance(uint256 amount); + error TooLowBalance(uint256 amount); + error ServiceAgreementHasBeenExpired( + bytes32 agreementId, + uint256 startTime, + uint16 epochsNumber, + uint128 epochLength + ); + error CommitWindowClosed( + bytes32 agreementId, + uint16 epoch, + uint256 commitWindowOpen, + uint256 commitWindowClose, + uint256 timeNow + ); + error NodeNotInShardingTable(uint72 identityId, bytes nodeId, uint96 ask, uint96 stake); + error ProofWindowClosed( + bytes32 agreementId, + uint16 epoch, + uint256 proofWindowOpen, + uint256 proofWindowClose, + uint256 timeNow + ); + error NodeAlreadyRewarded(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); + error NodeNotAwarded(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId, uint8 rank); + error WrongMerkleProof( + bytes32 agreementId, + uint16 epoch, + uint72 identityId, + bytes nodeId, + bytes32[] merkleProof, + bytes32 merkleRoot, + bytes32 chunkHash, + uint256 challenge + ); + error NodeAlreadySubmittedCommit(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); + error WrongScoreFunctionId( + bytes32 agreementId, + uint16 epoch, + uint8 agreementScoreFunctionId, + uint8 expectedScoreFunctionId, + uint256 timeNow + ); +} diff --git a/contracts/v1/structs/ServiceAgreementStructsV1.sol b/contracts/v1/structs/ServiceAgreementStructsV1.sol index b052f67c..22f4f3b7 100644 --- a/contracts/v1/structs/ServiceAgreementStructsV1.sol +++ b/contracts/v1/structs/ServiceAgreementStructsV1.sol @@ -54,6 +54,9 @@ library ServiceAgreementStructsV1 { bytes keyword; uint8 hashFunctionId; uint16 epoch; + uint72 closestNode; + uint72 leftNeighbourHoodEdge; + uint72 rightNeighbourHoodEdge; } struct ProofInputArgs { From 942f44424b8ca3834f8390f560320dd903304376 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 24 Jan 2024 11:40:46 +0100 Subject: [PATCH 14/46] Updated structure of the contracts --- contracts/v1/ScoringProxy.sol | 11 +-- .../v1/errors/ServiceAgreementErrorsV1.sol | 7 ++ .../v1/errors/ServiceAgreementErrorsV1New.sol | 56 -------------- .../CommitManagerV1U1.sol} | 39 +++++----- contracts/v2/ScoringProxy.sol | 73 +++++++++++++++++++ contracts/v2/ShardingTable.sol | 2 +- 6 files changed, 106 insertions(+), 82 deletions(-) delete mode 100644 contracts/v1/errors/ServiceAgreementErrorsV1New.sol rename contracts/{v1/CommitManagerV1New.sol => v2/CommitManagerV1U1.sol} (92%) create mode 100644 contracts/v2/ScoringProxy.sol diff --git a/contracts/v1/ScoringProxy.sol b/contracts/v1/ScoringProxy.sol index a20e29e3..83b0ddd5 100644 --- a/contracts/v1/ScoringProxy.sol +++ b/contracts/v1/ScoringProxy.sol @@ -44,15 +44,16 @@ contract ScoringProxy is Named, Versioned, ContractStatus { scoreFunctionSet.remove(scoreFunctionId); } - //remove everthring except id, noramlised distance, normalized stake function callScoreFunction( uint8 scoreFunctionId, - uint256 mappedDistance, - uint256 mappedStake + uint8 hashFunctionId, + bytes calldata nodeId, + bytes calldata keyword, + uint96 stake ) external view returns (uint40) { IScoreFunction scoringFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); - - return scoringFunction.calculateScore(mappedDistance, mappedStake); + uint256 distance = scoringFunction.calculateDistance(hashFunctionId, nodeId, keyword); + return scoringFunction.calculateScore(distance, stake); } function getScoreFunctionName(uint8 scoreFunctionId) external view returns (string memory) { diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1.sol index 0867f502..7f1fa693 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1.sol @@ -46,4 +46,11 @@ library ServiceAgreementErrorsV1 { uint256 challenge ); error NodeAlreadySubmittedCommit(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); + error WrongScoreFunctionId( + bytes32 agreementId, + uint16 epoch, + uint8 agreementScoreFunctionId, + uint8 expectedScoreFunctionId, + uint256 timeNow + ); } diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1New.sol b/contracts/v1/errors/ServiceAgreementErrorsV1New.sol deleted file mode 100644 index 1ac8ea15..00000000 --- a/contracts/v1/errors/ServiceAgreementErrorsV1New.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.16; - -library ServiceAgreementErrorsV1New { - error ServiceAgreementDoesntExist(bytes32 agreementId); - error EmptyAssetCreatorAddress(); - error AssetStorageNotInTheHub(address contractAddress); - error EmptyKeyword(); - error ZeroEpochsNumber(); - error ZeroTokenAmount(); - error ScoreFunctionDoesntExist(uint8 scoreFunctionId); - error TooLowAllowance(uint256 amount); - error TooLowBalance(uint256 amount); - error ServiceAgreementHasBeenExpired( - bytes32 agreementId, - uint256 startTime, - uint16 epochsNumber, - uint128 epochLength - ); - error CommitWindowClosed( - bytes32 agreementId, - uint16 epoch, - uint256 commitWindowOpen, - uint256 commitWindowClose, - uint256 timeNow - ); - error NodeNotInShardingTable(uint72 identityId, bytes nodeId, uint96 ask, uint96 stake); - error ProofWindowClosed( - bytes32 agreementId, - uint16 epoch, - uint256 proofWindowOpen, - uint256 proofWindowClose, - uint256 timeNow - ); - error NodeAlreadyRewarded(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); - error NodeNotAwarded(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId, uint8 rank); - error WrongMerkleProof( - bytes32 agreementId, - uint16 epoch, - uint72 identityId, - bytes nodeId, - bytes32[] merkleProof, - bytes32 merkleRoot, - bytes32 chunkHash, - uint256 challenge - ); - error NodeAlreadySubmittedCommit(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); - error WrongScoreFunctionId( - bytes32 agreementId, - uint16 epoch, - uint8 agreementScoreFunctionId, - uint8 expectedScoreFunctionId, - uint256 timeNow - ); -} diff --git a/contracts/v1/CommitManagerV1New.sol b/contracts/v2/CommitManagerV1U1.sol similarity index 92% rename from contracts/v1/CommitManagerV1New.sol rename to contracts/v2/CommitManagerV1U1.sol index 88e3ead7..e18d855a 100644 --- a/contracts/v1/CommitManagerV1New.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -2,28 +2,27 @@ pragma solidity ^0.8.16; -import {HashingProxy} from "./HashingProxy.sol"; -import {ScoringProxy} from "./ScoringProxy.sol"; -import {Staking} from "./Staking.sol"; +import {HashingProxy} from "../v1/HashingProxy.sol"; +import {ScoringProxy} from "../v1/ScoringProxy.sol"; +import {Staking} from "../v1/Staking.sol"; import {IdentityStorage} from "./storage/IdentityStorage.sol"; -import {ParametersStorage} from "./storage/ParametersStorage.sol"; -import {ProfileStorage} from "./storage/ProfileStorage.sol"; -import {ServiceAgreementStorageProxy} from "./storage/ServiceAgreementStorageProxy.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 "../v2/storage/ShardingTableStorage.sol"; -import {StakingStorage} from "./storage/StakingStorage.sol"; -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 {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 {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; -import {GeneralErrors} from "./errors/GeneralErrors.sol"; -import {ServiceAgreementErrorsV1} from "./errors/ServiceAgreementErrorsV1.sol"; -import {ServiceAgreementErrorsV1New} from "./errors/ServiceAgreementErrorsV1New.sol"; -import {ServiceAgreementStructsV1} from "./structs/ServiceAgreementStructsV1.sol"; +import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; +import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; +import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1.sol"; import {ShardingTableStructs} from "../v2/structs/ShardingTableStructs.sol"; -import {CommitManagerErrorsV1} from "./errors/CommitManagerErrorsV1.sol"; +import {CommitManagerErrorsV1} from "../v1/errors/CommitManagerErrorsV1.sol"; -contract CommitManagerV1New is Named, Versioned, ContractStatus, Initializable { +contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( address indexed assetContract, uint256 indexed tokenId, @@ -34,8 +33,8 @@ contract CommitManagerV1New is Named, Versioned, ContractStatus, Initializable { uint40 score ); - string private constant _NAME = "CommitManagerV1-new"; - string private constant _VERSION = "1.0.1"; + string private constant _NAME = "CommitManagerV1U1"; + string private constant _VERSION = "2.0.0"; bool[4] public reqs = [false, false, false, false]; @@ -189,7 +188,7 @@ contract CommitManagerV1New is Named, Versioned, ContractStatus, Initializable { uint8 agreementScoreFunctionId = sasProxy.getAgreementScoreFunctionId(agreementId); if (agreementScoreFunctionId != 2) { - revert ServiceAgreementErrorsV1New.WrongScoreFunctionId( + revert ServiceAgreementErrorsV1.WrongScoreFunctionId( agreementId, args.epoch, agreementScoreFunctionId, diff --git a/contracts/v2/ScoringProxy.sol b/contracts/v2/ScoringProxy.sol new file mode 100644 index 00000000..5451f9d5 --- /dev/null +++ b/contracts/v2/ScoringProxy.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {ContractStatus} from "../v1/abstract/ContractStatus.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 ScoringProxyV2 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 = "1.0.1"; + + 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); + } + + //remove everthring except id, noramlised distance, normalized stake + function callScoreFunction( + uint8 scoreFunctionId, + uint256 mappedDistance, + uint256 mappedStake + ) external view returns (uint40) { + IScoreFunction scoringFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); + + return scoringFunction.calculateScore(mappedDistance, mappedStake); + } + + 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 index 5e0dbc93..d3f58395 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -14,7 +14,7 @@ import {ShardingTableErrors} from "./errors/ShardingTableErrors.sol"; import {NULL} from "../v1/constants/ShardingTableConstants.sol"; -contract ShardingTable is Named, Versioned, ContractStatus, Initializable { +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); From dac7a273efbbc37b67cf15f85f5bcff4d901d2b9 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 24 Jan 2024 11:51:37 +0100 Subject: [PATCH 15/46] Moved new contracts to v2, naming changes --- .../v1/errors/ServiceAgreementErrorsV1.sol | 7 -- .../v1/structs/ServiceAgreementStructsV1.sol | 3 - contracts/v2/CommitManagerV1U1.sol | 80 +++++++++---------- .../errors/CommitManagerErrorsV2.sol} | 14 ++-- .../v2/errors/ServiceAgreementErrorsV2.sol | 13 +++ .../scoring/LinearSum.sol} | 15 ++-- .../v2/structs/ServiceAgreementStructsV2.sol | 71 ++++++++++++++++ 7 files changed, 137 insertions(+), 66 deletions(-) rename contracts/{v1/errors/CommitManagerErrorsV1.sol => v2/errors/CommitManagerErrorsV2.sol} (58%) create mode 100644 contracts/v2/errors/ServiceAgreementErrorsV2.sol rename contracts/{v1/scoring/linearSum.sol => v2/scoring/LinearSum.sol} (90%) create mode 100644 contracts/v2/structs/ServiceAgreementStructsV2.sol diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1.sol index 7f1fa693..0867f502 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1.sol @@ -46,11 +46,4 @@ library ServiceAgreementErrorsV1 { uint256 challenge ); error NodeAlreadySubmittedCommit(bytes32 agreementId, uint16 epoch, uint72 identityId, bytes nodeId); - error WrongScoreFunctionId( - bytes32 agreementId, - uint16 epoch, - uint8 agreementScoreFunctionId, - uint8 expectedScoreFunctionId, - uint256 timeNow - ); } diff --git a/contracts/v1/structs/ServiceAgreementStructsV1.sol b/contracts/v1/structs/ServiceAgreementStructsV1.sol index 22f4f3b7..b052f67c 100644 --- a/contracts/v1/structs/ServiceAgreementStructsV1.sol +++ b/contracts/v1/structs/ServiceAgreementStructsV1.sol @@ -54,9 +54,6 @@ library ServiceAgreementStructsV1 { bytes keyword; uint8 hashFunctionId; uint16 epoch; - uint72 closestNode; - uint72 leftNeighbourHoodEdge; - uint72 rightNeighbourHoodEdge; } struct ProofInputArgs { diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol index e18d855a..58d926cb 100644 --- a/contracts/v2/CommitManagerV1U1.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -18,9 +18,10 @@ import {Versioned} from "../v1/interface/Versioned.sol"; import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; -import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1.sol"; +import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; +import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; import {ShardingTableStructs} from "../v2/structs/ShardingTableStructs.sol"; -import {CommitManagerErrorsV1} from "../v1/errors/CommitManagerErrorsV1.sol"; +import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( @@ -108,7 +109,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function getTopCommitSubmissions( bytes32 agreementId, uint16 epoch - ) external view returns (ServiceAgreementStructsV1.CommitSubmission[] memory) { + ) external view returns (ServiceAgreementStructsV2.CommitSubmission[] memory) { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; if (!sasProxy.agreementV1Exists(agreementId)) @@ -123,8 +124,8 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint32 r0 = parametersStorage.r0(); - ServiceAgreementStructsV1.CommitSubmission[] - memory epochCommits = new ServiceAgreementStructsV1.CommitSubmission[](r0); + ServiceAgreementStructsV2.CommitSubmission[] + memory epochCommits = new ServiceAgreementStructsV2.CommitSubmission[](r0); bytes32 epochSubmissionsHead = sasProxy.getV1AgreementEpochSubmissionHead(agreementId, epoch); @@ -147,7 +148,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { return epochCommits; } - function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; bytes32 agreementId = hashingProxy.callHashFunction( @@ -188,7 +189,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint8 agreementScoreFunctionId = sasProxy.getAgreementScoreFunctionId(agreementId); if (agreementScoreFunctionId != 2) { - revert ServiceAgreementErrorsV1.WrongScoreFunctionId( + revert ServiceAgreementErrorsV2.WrongScoreFunctionId( agreementId, args.epoch, agreementScoreFunctionId, @@ -208,45 +209,43 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); } - if (!shardingTableStorage.nodeExists(args.leftNeighbourHoodEdge)) { + if (!shardingTableStorage.nodeExists(args.leftNeighborhoodEdge)) { ProfileStorage ps = profileStorage; revert ServiceAgreementErrorsV1.NodeNotInShardingTable( - args.leftNeighbourHoodEdge, - ps.getNodeId(args.leftNeighbourHoodEdge), - ps.getAsk(args.leftNeighbourHoodEdge), - stakingStorage.totalStakes(args.leftNeighbourHoodEdge) + args.leftNeighborhoodEdge, + ps.getNodeId(args.leftNeighborhoodEdge), + ps.getAsk(args.leftNeighborhoodEdge), + stakingStorage.totalStakes(args.leftNeighborhoodEdge) ); } - if (!shardingTableStorage.nodeExists(args.rightNeighbourHoodEdge)) { + if (!shardingTableStorage.nodeExists(args.rightNeighborhoodEdge)) { ProfileStorage ps = profileStorage; revert ServiceAgreementErrorsV1.NodeNotInShardingTable( - args.rightNeighbourHoodEdge, - ps.getNodeId(args.rightNeighbourHoodEdge), - ps.getAsk(args.rightNeighbourHoodEdge), - stakingStorage.totalStakes(args.rightNeighbourHoodEdge) + args.rightNeighborhoodEdge, + ps.getNodeId(args.rightNeighborhoodEdge), + ps.getAsk(args.rightNeighborhoodEdge), + stakingStorage.totalStakes(args.rightNeighborhoodEdge) ); } ShardingTableStructs.Node memory closestNode = shardingTableStorage.getNode(args.closestNode); - ShardingTableStructs.Node memory leftNeighbourhoodEdge = shardingTableStorage.getNode( - args.leftNeighbourHoodEdge - ); - ShardingTableStructs.Node memory rightNeighbourhoodEdge = shardingTableStorage.getNode( - args.rightNeighbourHoodEdge + ShardingTableStructs.Node memory leftNeighborhoodEdge = shardingTableStorage.getNode(args.leftNeighborhoodEdge); + ShardingTableStructs.Node memory rightNeighborhoodEdge = shardingTableStorage.getNode( + args.rightNeighborhoodEdge ); - bool isBetween = (leftNeighbourhoodEdge.index > rightNeighbourhoodEdge.index) - ? ((closestNode.index > leftNeighbourhoodEdge.index) || (closestNode.index < rightNeighbourhoodEdge.index)) - : ((closestNode.index > leftNeighbourhoodEdge.index) && (closestNode.index < rightNeighbourhoodEdge.index)); + bool isBetween = (leftNeighborhoodEdge.index > rightNeighborhoodEdge.index) + ? ((closestNode.index > leftNeighborhoodEdge.index) || (closestNode.index < rightNeighborhoodEdge.index)) + : ((closestNode.index > leftNeighborhoodEdge.index) && (closestNode.index < rightNeighborhoodEdge.index)); if (!isBetween) { - revert CommitManagerErrorsV1.closestNodeNotInNeighbourhood( + revert CommitManagerErrorsV2.closestNodeNotInNeighborhood( agreementId, - args.leftNeighbourHoodEdge, - args.rightNeighbourHoodEdge, + args.leftNeighborhoodEdge, + args.rightNeighborhoodEdge, args.closestNode, args.epoch, block.timestamp @@ -254,11 +253,10 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { } uint256 numberOfNodes = shardingTableStorage.nodesCount(); - uint256 clockwiseDistance = (rightNeighbourhoodEdge.index + numberOfNodes - leftNeighbourhoodEdge.index) % + uint256 clockwiseDistance = (rightNeighborhoodEdge.index + numberOfNodes - leftNeighborhoodEdge.index) % + numberOfNodes; + uint256 counterclockwiseDistance = (leftNeighborhoodEdge.index + numberOfNodes - rightNeighborhoodEdge.index) % numberOfNodes; - uint256 counterclockwiseDistance = (leftNeighbourhoodEdge.index + - numberOfNodes - - rightNeighbourhoodEdge.index) % numberOfNodes; uint256 indexDistance = (clockwiseDistance < counterclockwiseDistance) ? clockwiseDistance @@ -266,10 +264,10 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { //distance between 20 nodes is 19 (this shold be constant) if (!(indexDistance == 19)) { - revert CommitManagerErrorsV1.negihbourhoodWrongSize( + revert CommitManagerErrorsV2.negihbourhoodWrongSize( agreementId, - args.leftNeighbourHoodEdge, - args.rightNeighbourHoodEdge, + args.leftNeighborhoodEdge, + args.rightNeighborhoodEdge, numberOfNodes, 20, indexDistance, @@ -278,17 +276,17 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); } - uint256 hashRingNeighbourhoodDistance = calculateHashRingDistance( - leftNeighbourhoodEdge.hashRingPosition, - rightNeighbourhoodEdge.hashRingPosition + uint256 hashRingNeighborhoodDistance = calculateHashRingDistance( + leftNeighborhoodEdge.hashRingPosition, + rightNeighborhoodEdge.hashRingPosition ); bytes32 keywordHash = hashingProxy.callHashFunction(args.hashFunctionId, args.keyword); bytes32 nodeIdHash = hashingProxy.callHashFunction(args.hashFunctionId, profileStorage.getNodeId(identityId)); uint256 distance = calculateHashRingDistance( - leftNeighbourhoodEdge.hashRingPosition, - rightNeighbourhoodEdge.hashRingPosition + leftNeighborhoodEdge.hashRingPosition, + rightNeighborhoodEdge.hashRingPosition ); // uint40 score = scoringProxy.callScoreFunction( @@ -360,7 +358,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { sasProxy.createV1CommitSubmissionObject(commitId, identityId, prevIdentityId, nextIdentityId, score); - ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); + ServiceAgreementStructsV2.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); if ((i == 0) && (refCommit.identityId == 0)) { // No head -> Setting new head diff --git a/contracts/v1/errors/CommitManagerErrorsV1.sol b/contracts/v2/errors/CommitManagerErrorsV2.sol similarity index 58% rename from contracts/v1/errors/CommitManagerErrorsV1.sol rename to contracts/v2/errors/CommitManagerErrorsV2.sol index 8242df6f..5b0523ba 100644 --- a/contracts/v1/errors/CommitManagerErrorsV1.sol +++ b/contracts/v2/errors/CommitManagerErrorsV2.sol @@ -2,19 +2,19 @@ pragma solidity ^0.8.16; -library CommitManagerErrorsV1 { - error closestNodeNotInNeighbourhood( +library CommitManagerErrorsV2 { + error ClosestNodeNotInNeighborhood( bytes32 agreementId, - uint72 leftNeighbourhoodEdge, - uint72 rightNeighbourhoodEdge, + uint72 leftNeighborhoodEdge, + uint72 rightNeighborhoodEdge, uint72 closestNode, uint16 epoch, uint256 timeNow ); - error negihbourhoodWrongSize( + error NegihbourhoodWrongSize( bytes32 agreementId, - uint72 leftNeighbourhoodEdge, - uint72 rightNeighbourhoodEdge, + uint72 leftNeighborhoodEdge, + uint72 rightNeighborhoodEdge, uint256 numberOfNodes, uint256 negihbourhoodExpectedSize, uint256 negihbourhoodActualSize, diff --git a/contracts/v2/errors/ServiceAgreementErrorsV2.sol b/contracts/v2/errors/ServiceAgreementErrorsV2.sol new file mode 100644 index 00000000..58d39914 --- /dev/null +++ b/contracts/v2/errors/ServiceAgreementErrorsV2.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ServiceAgreementErrorsV2 { + error WrongScoreFunctionId( + bytes32 agreementId, + uint16 epoch, + uint8 agreementScoreFunctionId, + uint8 expectedScoreFunctionId, + uint256 timeNow + ); +} diff --git a/contracts/v1/scoring/linearSum.sol b/contracts/v2/scoring/LinearSum.sol similarity index 90% rename from contracts/v1/scoring/linearSum.sol rename to contracts/v2/scoring/LinearSum.sol index 42f202d1..b82ea6d1 100644 --- a/contracts/v1/scoring/linearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -2,16 +2,15 @@ pragma solidity ^0.8.16; -import {HashingProxy} from "../HashingProxy.sol"; -import {ParametersStorage} from "../storage/ParametersStorage.sol"; -import {HubDependent} from "../abstract/HubDependent.sol"; -import {Indexable} from "../interface/Indexable.sol"; -import {Initializable} from "../interface/Initializable.sol"; -import {IScoreFunction} from "../interface/IScoreFunction.sol"; -import {Named} from "../interface/Named.sol"; +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 {IScoreFunction} from "../../v1/interface/IScoreFunction.sol"; +import {Named} from "../../v1/interface/Named.sol"; import {PRBMathUD60x18} from "@prb/math/contracts/PRBMathUD60x18.sol"; -// Logarithmic Polynomial Long Division Score Function contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializable { using PRBMathUD60x18 for uint256; diff --git a/contracts/v2/structs/ServiceAgreementStructsV2.sol b/contracts/v2/structs/ServiceAgreementStructsV2.sol new file mode 100644 index 00000000..c59eedd9 --- /dev/null +++ b/contracts/v2/structs/ServiceAgreementStructsV2.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library ServiceAgreementStructsV2 { + struct CommitSubmission { + uint72 identityId; + uint72 prevIdentityId; + uint72 nextIdentityId; + uint40 score; + } + + struct ServiceAgreementInputArgs { + address assetCreator; + address assetContract; + uint256 tokenId; + bytes keyword; + uint8 hashFunctionId; + uint16 epochsNumber; + uint96 tokenAmount; + uint8 scoreFunctionId; + } + + struct ServiceAgreement { + uint256 startTime; + uint16 epochsNumber; + uint128 epochLength; + uint96 tokenAmount; + uint8 scoreFunctionId; + uint8 proofWindowOffsetPerc; + // epoch => headCommitId + mapping(uint16 => bytes32) epochSubmissionHeads; + // epoch => number of nodes received rewards + mapping(uint16 => uint32) rewardedNodesNumber; + } + + struct ExtendedServiceAgreement { + uint256 startTime; + uint16 epochsNumber; + uint128 epochLength; + uint96 tokenAmount; + uint96 updateTokenAmount; + uint8 scoreFunctionId; + uint8 proofWindowOffsetPerc; + // keccak256(epoch + stateIndex) => headCommitId + mapping(bytes32 => bytes32) epochSubmissionHeads; + // epoch => number of nodes received rewards + mapping(uint16 => uint32) rewardedNodesNumber; + } + + struct CommitInputArgs { + address assetContract; + uint256 tokenId; + bytes keyword; + uint8 hashFunctionId; + uint16 epoch; + uint72 closestNode; + uint72 leftNeighborhoodEdge; + uint72 rightNeighborhoodEdge; + } + + struct ProofInputArgs { + address assetContract; + uint256 tokenId; + bytes keyword; + uint8 hashFunctionId; + uint16 epoch; + bytes32[] proof; + bytes32 chunkHash; + } +} From 0aac2ab099888258e452e777561c11d8ef35a432 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 24 Jan 2024 15:43:17 +0100 Subject: [PATCH 16/46] Renamed Sharding Table structs, added possibility to add Node to the Sharding Table using Binary Search for position search --- contracts/v1/ShardingTable.sol | 20 +-- ...Structs.sol => ShardingTableStructsV1.sol} | 2 +- contracts/v2/CommitManagerV1U1.sol | 2 +- contracts/v2/ShardingTable.sol | 141 +++++++++++------- contracts/v2/scoring/LinearSum.sol | 23 --- contracts/v2/storage/ShardingTableStorage.sol | 28 +++- ...Structs.sol => ShardingTableStructsV2.sol} | 3 +- 7 files changed, 125 insertions(+), 94 deletions(-) rename contracts/v1/structs/{ShardingTableStructs.sol => ShardingTableStructsV1.sol} (89%) rename contracts/v2/structs/{ShardingTableStructs.sol => ShardingTableStructsV2.sol} (83%) diff --git a/contracts/v1/ShardingTable.sol b/contracts/v1/ShardingTable.sol index 4d7f7936..38266024 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(); @@ -133,24 +133,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 +162,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/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/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol index 58d926cb..ce49760c 100644 --- a/contracts/v2/CommitManagerV1U1.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -20,7 +20,7 @@ import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; -import {ShardingTableStructs} from "../v2/structs/ShardingTableStructs.sol"; +import {ShardingTableStructsV2} from "../v2/structs/ShardingTableStructsV2.sol"; import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index d3f58395..ec2d7e0a 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -9,7 +9,7 @@ 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 {ShardingTableStructs} from "./structs/ShardingTableStructs.sol"; +import {ShardingTableStructsV2} from "./structs/ShardingTableStructsV2.sol"; import {ShardingTableErrors} from "./errors/ShardingTableErrors.sol"; import {NULL} from "../v1/constants/ShardingTableConstants.sol"; @@ -45,25 +45,90 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { function getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) external view returns (ShardingTableStructs.NodeInfo[] memory) { + ) external view returns (ShardingTableStructsV2.NodeInfo[] memory) { return _getShardingTable(startingIdentityId, nodesNumber); } - function getShardingTable() external view returns (ShardingTableStructs.NodeInfo[] memory) { + function getShardingTable() external view returns (ShardingTableStructsV2.NodeInfo[] memory) { ShardingTableStorageV2 sts = shardingTableStorage; return _getShardingTable(sts.head(), sts.nodesCount()); } + function insertNode(uint72 identityId) external onlyContracts { + uint256 newNodeHashRingPosition = uint256(profileStorage.getNodeAddress(identityId, 1)); + (uint72 prevIdentityId, uint72 nextIdentityId) = _binarySearchForPosition(newNodeHashRingPosition); + + _insertNode(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId); + } + function insertNode(uint72 identityId, uint72 prevIdentityId, uint72 nextIdentityId) external onlyContracts { - ProfileStorage ps = profileStorage; + _insertNode(uint256(profileStorage.getNodeAddress(identityId, 1)), identityId, prevIdentityId, nextIdentityId); + } + + function removeNode(uint72 identityId) external onlyContracts { + ShardingTableStorageV2 sts = shardingTableStorage; - uint256 newNodeHashRingPosition = uint256(ps.getNodeAddress(identityId, 1)); + ShardingTableStructsV2.Node memory nodeToRemove = sts.getNode(identityId); - require(newNodeHashRingPosition != 0, "Profile doesn't exist"); + sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); + + uint72 index = nodeToRemove.index; + uint72 nextIdentityId = nodeToRemove.nextIdentityId; + while (nextIdentityId != NULL) { + sts.decrementNodeIndex(nextIdentityId); + sts.setIdentityId(index, nextIdentityId); + unchecked { + index += 1; + } + nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + } + + sts.deleteNodeObject(identityId); + sts.decrementNodesCount(); + + emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); + } + + function _binarySearchForPosition(uint256 hashRingPosition) internal virtual returns (uint72, uint72) { ShardingTableStorageV2 sts = shardingTableStorage; - ShardingTableStructs.Node memory prevNode = sts.getNode(prevIdentityId); + uint72 left = 0; + uint72 mid; + uint72 right = sts.nodesCount() - 1; + + uint72 prevIdentityId; + uint72 nextIdentityId; + while (left <= right) { + mid = (left + right) / 2; + ShardingTableStructsV2.Node memory currentNode = sts.getNodeByIndex(mid); + + if (currentNode.hashRingPosition < hashRingPosition) { + prevIdentityId = currentNode.identityId; + left = mid + 1; + } else if (currentNode.hashRingPosition > hashRingPosition) { + nextIdentityId = currentNode.identityId; + right = mid - 1; + } else { + prevIdentityId = currentNode.identityId; + nextIdentityId = currentNode.nextIdentityId; + break; + } + } + + return (prevIdentityId, nextIdentityId); + } + + function _insertNode( + uint256 newNodeHashRingPosition, + uint72 identityId, + uint72 prevIdentityId, + uint72 nextIdentityId + ) internal virtual { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + + ShardingTableStructsV2.Node memory prevNode = sts.getNode(prevIdentityId); if (prevNode.hashRingPosition > newNodeHashRingPosition) { revert ShardingTableErrors.InvalidPreviousIdentityId( @@ -74,7 +139,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ); } - ShardingTableStructs.Node memory nextNode = sts.getNode(nextIdentityId); + ShardingTableStructsV2.Node memory nextNode = sts.getNode(nextIdentityId); if (nextNode.identityId != NULL && nextNode.hashRingPosition < newNodeHashRingPosition) { revert ShardingTableErrors.InvalidNextIdentityId( @@ -95,13 +160,8 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ); } - sts.createNodeObject( - uint256(ps.getNodeAddress(identityId, 1)), - nextNode.index, - identityId, - prevIdentityId, - nextIdentityId - ); + sts.createNodeObject(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId, nextNode.index); + sts.setIdentityId(nextNode.index, identityId); sts.incrementNodesCount(); if (prevIdentityId == NULL) { @@ -114,8 +174,14 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { sts.link(identityId, nextIdentityId); } + uint72 index = nextNode.index + 1; while (nextIdentityId != NULL) { sts.incrementNodeIndex(nextIdentityId); + sts.setIdentityId(index, nextIdentityId); + + unchecked { + index += 1; + } nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; } @@ -127,66 +193,39 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ); } - function removeNode(uint72 identityId) external onlyContracts { - ShardingTableStorageV2 sts = shardingTableStorage; - - ShardingTableStructs.Node memory nodeToRemove = sts.getNode(identityId); - - require(nodeToRemove.identityId != 0, "Profile doesn't exist"); - - sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); - - uint72 nextIdentityId = nodeToRemove.nextIdentityId; - while (nextIdentityId != NULL) { - sts.decrementNodeIndex(nextIdentityId); - nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; - } - - sts.deleteNodeObject(identityId); - sts.decrementNodesCount(); - - emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); - } - function _getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) internal view virtual returns (ShardingTableStructs.NodeInfo[] memory) { - ShardingTableStructs.NodeInfo[] memory nodesPage; + ) internal view virtual returns (ShardingTableStructsV2.NodeInfo[] memory) { + ShardingTableStructsV2.NodeInfo[] memory nodesPage; ShardingTableStorageV2 sts = shardingTableStorage; if ((sts.nodesCount() == 0) || (nodesNumber == 0)) { return nodesPage; } - ShardingTableStructs.Node memory startingNode = sts.getNode(startingIdentityId); + ShardingTableStructsV2.Node memory startingNode = sts.getNode(startingIdentityId); require((startingIdentityId == NULL) || (startingNode.identityId != NULL), "Wrong starting Identity ID"); - nodesPage = new ShardingTableStructs.NodeInfo[](nodesNumber); + nodesPage = new ShardingTableStructsV2.NodeInfo[](nodesNumber); ProfileStorage ps = profileStorage; StakingStorage ss = stakingStorage; - nodesPage[0] = ShardingTableStructs.NodeInfo({ - nodeId: ps.getNodeId(startingIdentityId), - identityId: startingIdentityId, - ask: ps.getAsk(startingNode.identityId), - stake: ss.totalStakes(startingNode.identityId) - }); - uint72 nextIdentityId = startingIdentityId; - uint72 i = 1; + uint72 i; while ((i < nodesNumber) && (nextIdentityId != NULL)) { - nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; - - nodesPage[i] = ShardingTableStructs.NodeInfo({ + nodesPage[i] = ShardingTableStructsV2.NodeInfo({ + hashRingPosition: uint256(ps.getNodeAddress(nextIdentityId, 1)), nodeId: ps.getNodeId(nextIdentityId), identityId: nextIdentityId, ask: ps.getAsk(nextIdentityId), stake: ss.totalStakes(nextIdentityId) }); + nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + unchecked { i += 1; } diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index b82ea6d1..ae041494 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -70,29 +70,6 @@ contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializa return uint256(nodeIdHash ^ keywordHash); } - function calculateClockwiseProximityOnHashRing( - uint8 hashFunctionId, - bytes calldata nodeId, - bytes calldata keyword - ) external view returns (uint256) { - HashingProxy hp = hashingProxy; - bytes32 nodeIdHash = hp.callHashFunction(hashFunctionId, nodeId); - bytes32 keywordHash = hp.callHashFunction(hashFunctionId, keyword); - - uint256 peerPositionOnHashRing = uint256(nodeIdHash); - uint256 keyPositionOnHashRing = uint256(keywordHash); - - uint256 clockwiseDistance; - if (peerPositionOnHashRing > keyPositionOnHashRing) { - uint256 distanceToEnd = HASH_RING_SIZE - peerPositionOnHashRing; - clockwiseDistance = distanceToEnd + keyPositionOnHashRing; - } else { - clockwiseDistance = keyPositionOnHashRing - peerPositionOnHashRing; - } - - return clockwiseDistance; - } - function calculateBidirectionalProximityOnHashRing( uint8 hashFunctionId, bytes calldata peerHash, diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 809a4fca..6291cb38 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -5,7 +5,7 @@ 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 {ShardingTableStructs} from "../structs/ShardingTableStructs.sol"; +import {ShardingTableStructsV2} from "../structs/ShardingTableStructsV2.sol"; import {NULL} from "../../v1/constants/ShardingTableConstants.sol"; contract ShardingTableStorageV2 is Named, Versioned, HubDependent { @@ -16,7 +16,9 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { uint72 public nodesCount; // identityId => Node - mapping(uint72 => ShardingTableStructs.Node) internal nodes; + mapping(uint72 => ShardingTableStructsV2.Node) internal nodes; + // index => identityId + mapping(uint72 => uint72) internal identityIds; constructor(address hubAddress) HubDependent(hubAddress) { head = NULL; @@ -49,7 +51,7 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { uint72 nextIdentityId, uint72 index ) external onlyContracts { - nodes[identityId] = ShardingTableStructs.Node({ + nodes[identityId] = ShardingTableStructsV2.Node({ hashRingPosition: hashRingPosition, index: index, identityId: identityId, @@ -58,7 +60,7 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { }); } - function getNode(uint72 identityId) external view returns (ShardingTableStructs.Node memory) { + function getNode(uint72 identityId) external view returns (ShardingTableStructsV2.Node memory) { return nodes[identityId]; } @@ -86,13 +88,25 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { nodes[identityId].index -= 1; } + function getIdentityIdByIndex(uint72 index) external view returns (uint72) { + return identityIds[index]; + } + + function setIdentityId(uint72 index, uint72 identityId) external onlyContracts { + identityIds[index] = identityId; + } + + function getNodeByIndex(uint72 index) external view returns (ShardingTableStructsV2.Node memory) { + return nodes[identityIds[index]]; + } + function getMultipleNodes( uint72 firstIdentityId, uint16 nodesNumber - ) external view returns (ShardingTableStructs.Node[] memory) { - ShardingTableStructs.Node[] memory nodesPage = new ShardingTableStructs.Node[](nodesNumber); + ) external view returns (ShardingTableStructsV2.Node[] memory) { + ShardingTableStructsV2.Node[] memory nodesPage = new ShardingTableStructsV2.Node[](nodesNumber); - ShardingTableStructs.Node memory currentNode = nodes[firstIdentityId]; + ShardingTableStructsV2.Node memory currentNode = nodes[firstIdentityId]; for (uint256 i; i < nodesNumber; ) { nodesPage[i] = currentNode; currentNode = nodes[currentNode.nextIdentityId]; diff --git a/contracts/v2/structs/ShardingTableStructs.sol b/contracts/v2/structs/ShardingTableStructsV2.sol similarity index 83% rename from contracts/v2/structs/ShardingTableStructs.sol rename to contracts/v2/structs/ShardingTableStructsV2.sol index 20845946..7b8268b8 100644 --- a/contracts/v2/structs/ShardingTableStructs.sol +++ b/contracts/v2/structs/ShardingTableStructsV2.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.16; -library ShardingTableStructs { +library ShardingTableStructsV2 { struct NodeInfo { + uint256 hashRingPosition; bytes nodeId; uint72 identityId; uint96 ask; From c2d043533f2f6beabed23a9bce1827e47a55d19c Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 24 Jan 2024 16:24:42 +0100 Subject: [PATCH 17/46] Made indexes inside binary search signed integers --- contracts/v2/ShardingTable.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index ec2d7e0a..f317eaaf 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -93,15 +93,15 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { function _binarySearchForPosition(uint256 hashRingPosition) internal virtual returns (uint72, uint72) { ShardingTableStorageV2 sts = shardingTableStorage; - uint72 left = 0; - uint72 mid; - uint72 right = sts.nodesCount() - 1; + int72 left; + int72 mid; + int72 right = int72(sts.nodesCount()) - 1; uint72 prevIdentityId; uint72 nextIdentityId; while (left <= right) { mid = (left + right) / 2; - ShardingTableStructsV2.Node memory currentNode = sts.getNodeByIndex(mid); + ShardingTableStructsV2.Node memory currentNode = sts.getNodeByIndex(uint72(mid)); if (currentNode.hashRingPosition < hashRingPosition) { prevIdentityId = currentNode.identityId; From 122b31834b56346725e1457028892d1b338d259e Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 24 Jan 2024 18:08:53 +0100 Subject: [PATCH 18/46] Added custom errors for Staking contract, exposed addStake function for delegators, added usage of ShardingTableV2 for StakingV2 --- contracts/v1/ServiceAgreementV1.sol | 9 +- contracts/v1/errors/GeneralErrors.sol | 1 + .../v1/errors/ServiceAgreementErrorsV1.sol | 2 - .../v1/errors/ServiceAgreementErrorsV1U1.sol | 2 - contracts/v1/errors/TokenErrors.sol | 8 + contracts/v1/storage/ShardingTableStorage.sol | 14 +- contracts/v2/Staking.sol | 269 ++++++++++++++++++ contracts/v2/errors/ProfileErrors.sol | 7 + contracts/v2/errors/StakingErrors.sol | 11 + 9 files changed, 308 insertions(+), 15 deletions(-) create mode 100644 contracts/v1/errors/TokenErrors.sol create mode 100644 contracts/v2/Staking.sol create mode 100644 contracts/v2/errors/ProfileErrors.sol create mode 100644 contracts/v2/errors/StakingErrors.sol diff --git a/contracts/v1/ServiceAgreementV1.sol b/contracts/v1/ServiceAgreementV1.sol index bf71bd4b..71582bab 100644 --- a/contracts/v1/ServiceAgreementV1.sol +++ b/contracts/v1/ServiceAgreementV1.sol @@ -16,6 +16,7 @@ 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 {ServiceAgreementErrorsV1U1} from "./errors/ServiceAgreementErrorsV1U1.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -106,9 +107,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); @@ -253,9 +254,9 @@ contract ServiceAgreementV1 is Named, Versioned, ContractStatus, Initializable { 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/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..e35e2f3c 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, diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol index 32c93d30..691fca84 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol @@ -11,8 +11,6 @@ library ServiceAgreementErrorsV1U1 { error ZeroTokenAmount(); error ScoreFunctionDoesntExist(uint8 scoreFunctionId); error HashFunctionDoesntExist(uint8 hashFunctionId); - error TooLowAllowance(uint256 amount); - error TooLowBalance(uint256 amount); error ServiceAgreementHasBeenExpired( bytes32 agreementId, uint256 startTime, 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/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/v2/Staking.sol b/contracts/v2/Staking.sol new file mode 100644 index 00000000..f9b1edfc --- /dev/null +++ b/contracts/v2/Staking.sol @@ -0,0 +1,269 @@ +// 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 {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 {TokenErrors} from "../v1/errors/TokenErrors.sol"; +import {ProfileErrors} from "./errors/ProfileErrors.sol"; +import {StakingErrors} from "./errors/StakingErrors.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 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 AccumulatedOperatorFeeIncreased( + uint72 indexed identityId, + bytes nodeId, + uint96 oldAccumulatedOperatorFee, + uint96 newAccumulatedOperatorFee + ); + event OperatorFeeUpdated(uint72 indexed identityId, bytes nodeId, uint8 operatorFee); + + string private constant _NAME = "Staking"; + string private constant _VERSION = "1.0.2"; + + ShardingTableV2 public shardingTableContract; + IdentityStorageV2 public identityStorage; + 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")); + 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 + ); + } + + 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 (withdrawalTimestamp >= block.timestamp) { + revert StakingErrors.WithdrawalPeriodPending(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); + } + + // solhint-disable-next-line no-empty-blocks + function slash(uint72 identityId) external onlyContracts { + // TBD + } + + function setOperatorFee(uint72 identityId, uint8 operatorFee) external onlyAdmin(identityId) { + if (operatorFee > 100) { + revert StakingErrors.InvalidOperatorFee(); + } + stakingStorage.setOperatorFee(identityId, operatorFee); + + emit OperatorFeeUpdated(identityId, profileStorage.getNodeId(identityId), operatorFee); + } + + 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); + } + + 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/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/StakingErrors.sol b/contracts/v2/errors/StakingErrors.sol new file mode 100644 index 00000000..78f5e00e --- /dev/null +++ b/contracts/v2/errors/StakingErrors.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +library StakingErrors { + error ZeroSharesAmount(); + error WithdrawalWasntInitiated(); + error WithdrawalPeriodPending(uint256 endTimestamp); + error InvalidOperatorFee(); + error MaximumStakeExceeded(uint256 amount); +} From 7a86352765827c533098571ba15d79915a2800bc Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 00:26:37 +0100 Subject: [PATCH 19/46] Implemented distance/score calculation for LinearSum, adapted ScoringProxy to work with both old and new proximity-score function pairs --- ...mitManagerV1U1.sol => CommitManagerV2.sol} | 17 +-- ...ingProxy.sol => ProximityScoringProxy.sol} | 36 ++++-- .../IProximityScoreFunctionsPair.sol | 18 +++ contracts/v2/scoring/LinearSum.sol | 106 +++++++++++------- .../v2/structs/ServiceAgreementStructsV2.sol | 55 --------- 5 files changed, 124 insertions(+), 108 deletions(-) rename contracts/v2/{CommitManagerV1U1.sol => CommitManagerV2.sol} (96%) rename contracts/v2/{ScoringProxy.sol => ProximityScoringProxy.sol} (66%) create mode 100644 contracts/v2/interface/IProximityScoreFunctionsPair.sol diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV2.sol similarity index 96% rename from contracts/v2/CommitManagerV1U1.sol rename to contracts/v2/CommitManagerV2.sol index ce49760c..d99b60a1 100644 --- a/contracts/v2/CommitManagerV1U1.sol +++ b/contracts/v2/CommitManagerV2.sol @@ -19,6 +19,7 @@ import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.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"; @@ -109,7 +110,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function getTopCommitSubmissions( bytes32 agreementId, uint16 epoch - ) external view returns (ServiceAgreementStructsV2.CommitSubmission[] memory) { + ) external view returns (ServiceAgreementStructsV1.CommitSubmission[] memory) { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; if (!sasProxy.agreementV1Exists(agreementId)) @@ -124,8 +125,8 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint32 r0 = parametersStorage.r0(); - ServiceAgreementStructsV2.CommitSubmission[] - memory epochCommits = new ServiceAgreementStructsV2.CommitSubmission[](r0); + ServiceAgreementStructsV1.CommitSubmission[] + memory epochCommits = new ServiceAgreementStructsV1.CommitSubmission[](r0); bytes32 epochSubmissionsHead = sasProxy.getV1AgreementEpochSubmissionHead(agreementId, epoch); @@ -231,9 +232,11 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); } - ShardingTableStructs.Node memory closestNode = shardingTableStorage.getNode(args.closestNode); - ShardingTableStructs.Node memory leftNeighborhoodEdge = shardingTableStorage.getNode(args.leftNeighborhoodEdge); - ShardingTableStructs.Node memory rightNeighborhoodEdge = shardingTableStorage.getNode( + ShardingTableStructsV2.Node memory closestNode = shardingTableStorage.getNode(args.closestNode); + ShardingTableStructsV2.Node memory leftNeighborhoodEdge = shardingTableStorage.getNode( + args.leftNeighborhoodEdge + ); + ShardingTableStructsV2.Node memory rightNeighborhoodEdge = shardingTableStorage.getNode( args.rightNeighborhoodEdge ); @@ -358,7 +361,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { sasProxy.createV1CommitSubmissionObject(commitId, identityId, prevIdentityId, nextIdentityId, score); - ServiceAgreementStructsV2.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); + ServiceAgreementStructsV1.CommitSubmission memory refCommit = sasProxy.getCommitSubmission(refCommitId); if ((i == 0) && (refCommit.identityId == 0)) { // No head -> Setting new head diff --git a/contracts/v2/ScoringProxy.sol b/contracts/v2/ProximityScoringProxy.sol similarity index 66% rename from contracts/v2/ScoringProxy.sol rename to contracts/v2/ProximityScoringProxy.sol index 5451f9d5..63a34843 100644 --- a/contracts/v2/ScoringProxy.sol +++ b/contracts/v2/ProximityScoringProxy.sol @@ -3,12 +3,13 @@ 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 ScoringProxyV2 is Named, Versioned, ContractStatus { +contract ProximityScoringProxy is Named, Versioned, ContractStatus { using UnorderedIndexableContractDynamicSetLib for UnorderedIndexableContractDynamicSetLib.Set; event NewScoringFunctionContract(uint8 indexed scoreFunctionId, address newContractAddress); @@ -44,15 +45,36 @@ contract ScoringProxyV2 is Named, Versioned, ContractStatus { scoreFunctionSet.remove(scoreFunctionId); } - //remove everthring except id, noramlised distance, normalized stake + function callScoreFunction(uint8 scoreFunctionId, uint256 distance, uint96 stake) external view returns (uint40) { + IScoreFunction scoreFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); + return scoreFunction.calculateScore(distance, stake); + } + function callScoreFunction( uint8 scoreFunctionId, - uint256 mappedDistance, - uint256 mappedStake - ) external view returns (uint40) { - IScoreFunction scoringFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); + uint256 distance, + uint256 maxDistance, + uint72 maxNodesNumber, + uint96 stake + ) external view returns (uint64) { + IProximityScoreFunctionsPair proximityScoreFunctionsPair = IProximityScoreFunctionsPair( + scoreFunctionSet.get(scoreFunctionId).addr + ); + + return proximityScoreFunctionsPair.calculateScore(distance, maxDistance, maxNodesNumber, stake); + } - return scoringFunction.calculateScore(mappedDistance, mappedStake); + function callProximityFunction( + uint8 proximityFunctionId, + uint8 hashFunctionId, + bytes memory nodeId, + bytes memory 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) { diff --git a/contracts/v2/interface/IProximityScoreFunctionsPair.sol b/contracts/v2/interface/IProximityScoreFunctionsPair.sol new file mode 100644 index 00000000..9d30c1d4 --- /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 (uint64); + + 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 index ae041494..e0fe2f22 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -7,30 +7,27 @@ 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 {IScoreFunction} from "../../v1/interface/IScoreFunction.sol"; +import {IProximityScoreFunctionsPair} from "../interface/IProximityScoreFunctionsPair.sol"; import {Named} from "../../v1/interface/Named.sol"; -import {PRBMathUD60x18} from "@prb/math/contracts/PRBMathUD60x18.sol"; - -contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializable { - using PRBMathUD60x18 for uint256; +contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDependent, Initializable { event ParameterChanged(string parameterName, uint256 parameterValue); uint8 private constant _ID = 2; string private constant _NAME = "LinearSum"; - HashingProxy public hashingProxy; - ParametersStorage public parametersStorage; - uint256 constant HASH_RING_SIZE = type(uint256).max; - uint256 public distanceScaleFactor; + HashingProxy public hashingProxy; + ParametersStorage public parametersStorage; + uint96 public distanceScaleFactor; + uint96 public stakeScaleFactor; uint32 public w1; uint32 public w2; constructor(address hubAddress) HubDependent(hubAddress) { - distanceScaleFactor = 1000000000000000000; + distanceScaleFactor = 1e18; w1 = 1; w2 = 1; } @@ -48,52 +45,85 @@ contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializa return _NAME; } - // TODO: Implement scoring function function calculateScore( - uint8 hashFunctionId, - bytes calldata nodeId, - bytes calldata keyword - ) external view returns (uint40) { - return 1; + uint256 distance, + uint256 maxDistance, + uint72 maxNodesNumber, + uint96 stake + ) external view returns (uint64) { + return ((1e18 - normalizeDistance(distance, maxDistance, maxNodesNumber)) * w1 + normalizeStake(stake) * w2); } - // TODO: Change this function calculateDistance( uint8 hashFunctionId, bytes calldata nodeId, bytes calldata keyword ) external view returns (uint256) { - HashingProxy hp = hashingProxy; - bytes32 nodeIdHash = hp.callHashFunction(hashFunctionId, nodeId); - bytes32 keywordHash = hp.callHashFunction(hashFunctionId, keyword); + uint256 nodePositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, nodeId)); + uint256 keywordPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyword)); - return uint256(nodeIdHash ^ keywordHash); + uint256 directDistance = ( + (nodePositionOnHashRing > keywordPositionOnHashRing) + ? nodePositionOnHashRing - keywordPositionOnHashRing + : keywordPositionOnHashRing - nodePositionOnHashRing + ); + + return (directDistance < HASH_RING_SIZE - directDistance) ? directDistance : HASH_RING_SIZE - directDistance; } - function calculateBidirectionalProximityOnHashRing( - uint8 hashFunctionId, - bytes calldata peerHash, - bytes calldata keyHash - ) external view returns (uint256) { - uint256 peerPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, peerHash)); - uint256 keyPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyHash)); - - uint256 directDistance; - if (peerPositionOnHashRing > keyPositionOnHashRing) { - directDistance = peerPositionOnHashRing - keyPositionOnHashRing; - } else { - directDistance = keyPositionOnHashRing - peerPositionOnHashRing; + function normalizeDistance( + uint256 distance, + uint256 maxDistance, + uint72 maxNodesNumber + ) public view returns (uint64) { + if (distance == 0) { + return 0; + } + + uint256 idealMaxDistance = (HASH_RING_SIZE / maxNodesNumber) * (parametersStorage.r2() / 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 wraparoundDistance = HASH_RING_SIZE - directDistance; + 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(); - return (directDistance < wraparoundDistance) ? directDistance : wraparoundDistance; + return uint64((uint256(distanceScaleFactor) * (stake - minStake)) / (maxStake - minStake)); } - function getParameters() external view returns (uint256, uint32, uint32) { + function getParameters() external view returns (uint192, uint32, uint32) { return (distanceScaleFactor, 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_; @@ -105,6 +135,4 @@ contract LinearSum is IScoreFunction, Indexable, Named, HubDependent, Initializa emit ParameterChanged("w2", w2); } - - function calculateScore(uint256 distance, uint96 stake) external view override returns (uint40) {} } diff --git a/contracts/v2/structs/ServiceAgreementStructsV2.sol b/contracts/v2/structs/ServiceAgreementStructsV2.sol index c59eedd9..33b7e26a 100644 --- a/contracts/v2/structs/ServiceAgreementStructsV2.sol +++ b/contracts/v2/structs/ServiceAgreementStructsV2.sol @@ -3,51 +3,6 @@ pragma solidity ^0.8.16; library ServiceAgreementStructsV2 { - struct CommitSubmission { - uint72 identityId; - uint72 prevIdentityId; - uint72 nextIdentityId; - uint40 score; - } - - struct ServiceAgreementInputArgs { - address assetCreator; - address assetContract; - uint256 tokenId; - bytes keyword; - uint8 hashFunctionId; - uint16 epochsNumber; - uint96 tokenAmount; - uint8 scoreFunctionId; - } - - struct ServiceAgreement { - uint256 startTime; - uint16 epochsNumber; - uint128 epochLength; - uint96 tokenAmount; - uint8 scoreFunctionId; - uint8 proofWindowOffsetPerc; - // epoch => headCommitId - mapping(uint16 => bytes32) epochSubmissionHeads; - // epoch => number of nodes received rewards - mapping(uint16 => uint32) rewardedNodesNumber; - } - - struct ExtendedServiceAgreement { - uint256 startTime; - uint16 epochsNumber; - uint128 epochLength; - uint96 tokenAmount; - uint96 updateTokenAmount; - uint8 scoreFunctionId; - uint8 proofWindowOffsetPerc; - // keccak256(epoch + stateIndex) => headCommitId - mapping(bytes32 => bytes32) epochSubmissionHeads; - // epoch => number of nodes received rewards - mapping(uint16 => uint32) rewardedNodesNumber; - } - struct CommitInputArgs { address assetContract; uint256 tokenId; @@ -58,14 +13,4 @@ library ServiceAgreementStructsV2 { uint72 leftNeighborhoodEdge; uint72 rightNeighborhoodEdge; } - - struct ProofInputArgs { - address assetContract; - uint256 tokenId; - bytes keyword; - uint8 hashFunctionId; - uint16 epoch; - bytes32[] proof; - bytes32 chunkHash; - } } From 0e1ce40eb9b267b8cf5a3d609e9957575a8f1a2a Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 02:16:46 +0100 Subject: [PATCH 20/46] Updated CommitManagerV2 --- ...ommitManagerV2.sol => CommitManagerV1.sol} | 274 +++++++++++------- contracts/v2/ProximityScoringProxy.sol | 6 +- contracts/v2/errors/CommitManagerErrorsV2.sol | 43 ++- .../v2/errors/ServiceAgreementErrorsV2.sol | 3 +- .../IProximityScoreFunctionsPair.sol | 2 +- contracts/v2/scoring/LinearSum.sol | 21 +- .../v2/structs/ServiceAgreementStructsV2.sol | 6 +- 7 files changed, 220 insertions(+), 135 deletions(-) rename contracts/v2/{CommitManagerV2.sol => CommitManagerV1.sol} (62%) diff --git a/contracts/v2/CommitManagerV2.sol b/contracts/v2/CommitManagerV1.sol similarity index 62% rename from contracts/v2/CommitManagerV2.sol rename to contracts/v2/CommitManagerV1.sol index d99b60a1..f758b25d 100644 --- a/contracts/v2/CommitManagerV2.sol +++ b/contracts/v2/CommitManagerV1.sol @@ -3,25 +3,25 @@ pragma solidity ^0.8.16; import {HashingProxy} from "../v1/HashingProxy.sol"; -import {ScoringProxy} from "../v1/ScoringProxy.sol"; -import {Staking} from "../v1/Staking.sol"; -import {IdentityStorage} from "./storage/IdentityStorage.sol"; +import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; +import {StakingV2} from "./Staking.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 "../v2/storage/ShardingTableStorage.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 {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.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"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { @@ -35,15 +35,17 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint40 score ); - string private constant _NAME = "CommitManagerV1U1"; + string private constant _NAME = "CommitManagerV1"; string private constant _VERSION = "2.0.0"; + uint8 private constant LOG2PLDSF_ID = 1; + bool[4] public reqs = [false, false, false, false]; HashingProxy public hashingProxy; - ScoringProxy public scoringProxy; - Staking public stakingContract; - IdentityStorage public identityStorage; + ProximityScoringProxy public proximityScoringProxy; + StakingV2 public stakingContract; + IdentityStorageV2 public identityStorage; ParametersStorage public parametersStorage; ProfileStorage public profileStorage; ServiceAgreementStorageProxy public serviceAgreementStorageProxy; @@ -57,9 +59,9 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); - scoringProxy = ScoringProxy(hub.getContractAddress("ScoringProxy")); - stakingContract = Staking(hub.getContractAddress("Staking")); - identityStorage = IdentityStorage(hub.getContractAddress("IdentityStorage")); + proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); + stakingContract = StakingV2(hub.getContractAddress("Staking")); + identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage")); serviceAgreementStorageProxy = ServiceAgreementStorageProxy( @@ -151,6 +153,8 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; bytes32 agreementId = hashingProxy.callHashFunction( args.hashFunctionId, @@ -160,6 +164,16 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { if (!sasProxy.agreementV1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); + + if (proximityScoreFunctionPairId == LOG2PLDSF_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + proximityScoreFunctionPairId, + block.timestamp + ); + if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); @@ -176,9 +190,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint72 identityId = identityStorage.getIdentityId(msg.sender); - if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { - ProfileStorage ps = profileStorage; - + if (!reqs[1] && !sts.nodeExists(identityId)) { revert ServiceAgreementErrorsV1.NodeNotInShardingTable( identityId, ps.getNodeId(identityId), @@ -187,116 +199,177 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); } - uint8 agreementScoreFunctionId = sasProxy.getAgreementScoreFunctionId(agreementId); + ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(args.closestNodeIndex); + ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(args.leftEdgeNodeIndex); + ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(args.rightEdgeNodeIndex); + + // Verify that closestNode is in smaller arc between leftNode and rightNode + bool isBetween = (leftEdgeNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + ? (closestNode.hashRingPosition >= leftEdgeNode.hashRingPosition && + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + : (leftEdgeNode.hashRingPosition <= closestNode.hashRingPosition || + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); - if (agreementScoreFunctionId != 2) { - revert ServiceAgreementErrorsV2.WrongScoreFunctionId( + if (!isBetween) { + revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( agreementId, args.epoch, - agreementScoreFunctionId, - 2, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex, block.timestamp ); } - if (!shardingTableStorage.nodeExists(args.closestNode)) { - ProfileStorage ps = profileStorage; - - revert ServiceAgreementErrorsV1.NodeNotInShardingTable( - args.closestNode, - ps.getNodeId(args.closestNode), - ps.getAsk(args.closestNode), - stakingStorage.totalStakes(args.closestNode) - ); - } - - if (!shardingTableStorage.nodeExists(args.leftNeighborhoodEdge)) { - ProfileStorage ps = profileStorage; + // Verify number of nodes between leftNode and rightNode (should be R2) + uint72 nodesCount = sts.nodesCount(); + uint72 nodesInBetweenClockwise = ( + (rightEdgeNode.index > leftEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index - 1 + : leftEdgeNode.index - rightEdgeNode.index - 1 + ); + uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) + ? nodesInBetweenClockwise + 2 + : nodesCount - nodesInBetweenClockwise; - revert ServiceAgreementErrorsV1.NodeNotInShardingTable( - args.leftNeighborhoodEdge, - ps.getNodeId(args.leftNeighborhoodEdge), - ps.getAsk(args.leftNeighborhoodEdge), - stakingStorage.totalStakes(args.leftNeighborhoodEdge) + if (neighborhoodSize != parametersStorage.r2()) { + revert CommitManagerErrorsV2.InvalidNeighborhoodSize( + agreementId, + args.epoch, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex, + nodesCount, + parametersStorage.r2(), + neighborhoodSize, + block.timestamp ); } - if (!shardingTableStorage.nodeExists(args.rightNeighborhoodEdge)) { - ProfileStorage ps = profileStorage; + // Verify that closestNode is indeed closest + uint256 closestDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(closestNode.identityId) + ); - revert ServiceAgreementErrorsV1.NodeNotInShardingTable( - args.rightNeighborhoodEdge, - ps.getNodeId(args.rightNeighborhoodEdge), - ps.getAsk(args.rightNeighborhoodEdge), - stakingStorage.totalStakes(args.rightNeighborhoodEdge) + if ( + closestDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(closestNode.prevIdentityId) + ) || + closestDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(closestNode.nextIdentityId) + ) + ) { + revert CommitManagerErrorsV2.InvalidClosestNode( + agreementId, + args.epoch, + args.closestNodeIndex, + closestDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(closestNode.prevIdentityId) + ), + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(closestNode.nextIdentityId) + ), + block.timestamp ); } - ShardingTableStructsV2.Node memory closestNode = shardingTableStorage.getNode(args.closestNode); - ShardingTableStructsV2.Node memory leftNeighborhoodEdge = shardingTableStorage.getNode( - args.leftNeighborhoodEdge - ); - ShardingTableStructsV2.Node memory rightNeighborhoodEdge = shardingTableStorage.getNode( - args.rightNeighborhoodEdge + // Verify that leftNode is indeed the left edge of the Neighborhood + uint256 leftEdgeDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(leftEdgeNode.identityId) ); - bool isBetween = (leftNeighborhoodEdge.index > rightNeighborhoodEdge.index) - ? ((closestNode.index > leftNeighborhoodEdge.index) || (closestNode.index < rightNeighborhoodEdge.index)) - : ((closestNode.index > leftNeighborhoodEdge.index) && (closestNode.index < rightNeighborhoodEdge.index)); - - if (!isBetween) { - revert CommitManagerErrorsV2.closestNodeNotInNeighborhood( + if ( + leftEdgeDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(rightEdgeNode.nextIdentityId) + ) + ) { + revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, - args.leftNeighborhoodEdge, - args.rightNeighborhoodEdge, - args.closestNode, args.epoch, + args.leftEdgeNodeIndex, + leftEdgeDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(rightEdgeNode.nextIdentityId) + ), block.timestamp ); } - uint256 numberOfNodes = shardingTableStorage.nodesCount(); - uint256 clockwiseDistance = (rightNeighborhoodEdge.index + numberOfNodes - leftNeighborhoodEdge.index) % - numberOfNodes; - uint256 counterclockwiseDistance = (leftNeighborhoodEdge.index + numberOfNodes - rightNeighborhoodEdge.index) % - numberOfNodes; - - uint256 indexDistance = (clockwiseDistance < counterclockwiseDistance) - ? clockwiseDistance - : counterclockwiseDistance; + // Verify that rightNode is indeed the right edge of the Neighborhood + uint256 rightEdgeDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(rightEdgeNode.identityId) + ); - //distance between 20 nodes is 19 (this shold be constant) - if (!(indexDistance == 19)) { - revert CommitManagerErrorsV2.negihbourhoodWrongSize( + if ( + rightEdgeDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(leftEdgeNode.prevIdentityId) + ) + ) { + revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, - args.leftNeighborhoodEdge, - args.rightNeighborhoodEdge, - numberOfNodes, - 20, - indexDistance, args.epoch, + args.rightEdgeNodeIndex, + rightEdgeDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(leftEdgeNode.prevIdentityId) + ), block.timestamp ); } - uint256 hashRingNeighborhoodDistance = calculateHashRingDistance( - leftNeighborhoodEdge.hashRingPosition, - rightNeighborhoodEdge.hashRingPosition + uint256 distance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + ps.getNodeId(identityId) ); - - bytes32 keywordHash = hashingProxy.callHashFunction(args.hashFunctionId, args.keyword); - bytes32 nodeIdHash = hashingProxy.callHashFunction(args.hashFunctionId, profileStorage.getNodeId(identityId)); - - uint256 distance = calculateHashRingDistance( - leftNeighborhoodEdge.hashRingPosition, - rightNeighborhoodEdge.hashRingPosition + uint256 maxDistance = (leftEdgeDistance > rightEdgeDistance) ? leftEdgeDistance : rightEdgeDistance; + + uint40 score = proximityScoringProxy.callScoreFunction( + proximityScoreFunctionPairId, + distance, + maxDistance, + nodesCount, + stakingStorage.totalStakes(identityId) ); - // uint40 score = scoringProxy.callScoreFunction( - // mappedDistance, - // mappedStake - // ); - _insertCommit(agreementId, args.epoch, identityId, 0, 0, score); emit CommitSubmitted( @@ -408,17 +481,4 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { leftIdentityId ); } - - function calculateHashRingDistance( - uint leftNodePositionOnHashRing, - uint rightNodePositionOnHashRing - ) private view returns (uint256) { - uint256 directDistance = (leftNodePositionOnHashRing >= rightNodePositionOnHashRing) - ? (leftNodePositionOnHashRing - rightNodePositionOnHashRing) - : (rightNodePositionOnHashRing - leftNodePositionOnHashRing); - - uint256 reverseDistance = HASH_RING_SIZE - directDistance; - - return (directDistance < reverseDistance) ? directDistance : reverseDistance; - } } diff --git a/contracts/v2/ProximityScoringProxy.sol b/contracts/v2/ProximityScoringProxy.sol index 63a34843..68bb9e14 100644 --- a/contracts/v2/ProximityScoringProxy.sol +++ b/contracts/v2/ProximityScoringProxy.sol @@ -54,14 +54,14 @@ contract ProximityScoringProxy is Named, Versioned, ContractStatus { uint8 scoreFunctionId, uint256 distance, uint256 maxDistance, - uint72 maxNodesNumber, + uint72 nodesCount, uint96 stake - ) external view returns (uint64) { + ) external view returns (uint40) { IProximityScoreFunctionsPair proximityScoreFunctionsPair = IProximityScoreFunctionsPair( scoreFunctionSet.get(scoreFunctionId).addr ); - return proximityScoreFunctionsPair.calculateScore(distance, maxDistance, maxNodesNumber, stake); + return proximityScoreFunctionsPair.calculateScore(distance, maxDistance, nodesCount, stake); } function callProximityFunction( diff --git a/contracts/v2/errors/CommitManagerErrorsV2.sol b/contracts/v2/errors/CommitManagerErrorsV2.sol index 5b0523ba..d93f8a20 100644 --- a/contracts/v2/errors/CommitManagerErrorsV2.sol +++ b/contracts/v2/errors/CommitManagerErrorsV2.sol @@ -5,20 +5,45 @@ pragma solidity ^0.8.16; library CommitManagerErrorsV2 { error ClosestNodeNotInNeighborhood( bytes32 agreementId, - uint72 leftNeighborhoodEdge, - uint72 rightNeighborhoodEdge, - uint72 closestNode, uint16 epoch, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex, uint256 timeNow ); - error NegihbourhoodWrongSize( + error InvalidNeighborhoodSize( bytes32 agreementId, - uint72 leftNeighborhoodEdge, - uint72 rightNeighborhoodEdge, - uint256 numberOfNodes, - uint256 negihbourhoodExpectedSize, - uint256 negihbourhoodActualSize, 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, + uint256 leftEdgeNodeDistance, + uint256 rightEdgeNodeAdjacentDistance, + uint256 timeNow + ); + error InvalidRightEdgeNode( + bytes32 agreementId, + uint16 epoch, + uint72 rightEdgeNodeIndex, + uint256 rightEdgeNodeDistance, + uint256 leftEdgeNodeAdjacentDistance, uint256 timeNow ); } diff --git a/contracts/v2/errors/ServiceAgreementErrorsV2.sol b/contracts/v2/errors/ServiceAgreementErrorsV2.sol index 58d39914..690a65ac 100644 --- a/contracts/v2/errors/ServiceAgreementErrorsV2.sol +++ b/contracts/v2/errors/ServiceAgreementErrorsV2.sol @@ -3,11 +3,10 @@ pragma solidity ^0.8.16; library ServiceAgreementErrorsV2 { - error WrongScoreFunctionId( + error InvalidProximityScoreFunctionsPairId( bytes32 agreementId, uint16 epoch, uint8 agreementScoreFunctionId, - uint8 expectedScoreFunctionId, uint256 timeNow ); } diff --git a/contracts/v2/interface/IProximityScoreFunctionsPair.sol b/contracts/v2/interface/IProximityScoreFunctionsPair.sol index 9d30c1d4..0410b290 100644 --- a/contracts/v2/interface/IProximityScoreFunctionsPair.sol +++ b/contracts/v2/interface/IProximityScoreFunctionsPair.sol @@ -8,7 +8,7 @@ interface IProximityScoreFunctionsPair { uint256 maxDistance, uint72 maxNodesNumber, uint96 stake - ) external view returns (uint64); + ) external view returns (uint40); function calculateDistance( uint8 hashFunctionId, diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index e0fe2f22..4b7e47eb 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -50,8 +50,9 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint256 maxDistance, uint72 maxNodesNumber, uint96 stake - ) external view returns (uint64) { - return ((1e18 - normalizeDistance(distance, maxDistance, maxNodesNumber)) * w1 + normalizeStake(stake) * w2); + ) external view returns (uint40) { + return + uint40((1e18 - normalizeDistance(distance, maxDistance, maxNodesNumber)) * w1 + normalizeStake(stake) * w2); } function calculateDistance( @@ -62,25 +63,25 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint256 nodePositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, nodeId)); uint256 keywordPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyword)); - uint256 directDistance = ( + uint256 distanceClockwise = ( (nodePositionOnHashRing > keywordPositionOnHashRing) ? nodePositionOnHashRing - keywordPositionOnHashRing : keywordPositionOnHashRing - nodePositionOnHashRing ); - return (directDistance < HASH_RING_SIZE - directDistance) ? directDistance : HASH_RING_SIZE - directDistance; + return ( + (distanceClockwise < HASH_RING_SIZE - distanceClockwise) + ? distanceClockwise + : HASH_RING_SIZE - distanceClockwise + ); } - function normalizeDistance( - uint256 distance, - uint256 maxDistance, - uint72 maxNodesNumber - ) public view returns (uint64) { + function normalizeDistance(uint256 distance, uint256 maxDistance, uint72 nodesCount) public view returns (uint64) { if (distance == 0) { return 0; } - uint256 idealMaxDistance = (HASH_RING_SIZE / maxNodesNumber) * (parametersStorage.r2() / 2); + uint256 idealMaxDistance = (HASH_RING_SIZE / nodesCount) * (parametersStorage.r2() / 2); uint256 divisor = (maxDistance <= idealMaxDistance) ? maxDistance : idealMaxDistance; uint256 maxMultiplier = type(uint256).max / distance; diff --git a/contracts/v2/structs/ServiceAgreementStructsV2.sol b/contracts/v2/structs/ServiceAgreementStructsV2.sol index 33b7e26a..67e47c3e 100644 --- a/contracts/v2/structs/ServiceAgreementStructsV2.sol +++ b/contracts/v2/structs/ServiceAgreementStructsV2.sol @@ -9,8 +9,8 @@ library ServiceAgreementStructsV2 { bytes keyword; uint8 hashFunctionId; uint16 epoch; - uint72 closestNode; - uint72 leftNeighborhoodEdge; - uint72 rightNeighborhoodEdge; + uint72 closestNodeIndex; + uint72 leftEdgeNodeIndex; + uint72 rightEdgeNodeIndex; } } From b50ca3e763d89d1ae075d84ccf3856840343e26a Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 09:59:48 +0100 Subject: [PATCH 21/46] Added CommitManagerV2U1, formatting fixes --- contracts/v1/CommitManagerV1.sol | 17 +- contracts/v1/CommitManagerV1U1.sol | 45 +- contracts/v1/HubController.sol | 16 +- contracts/v1/Identity.sol | 8 +- contracts/v1/Profile.sol | 4 +- contracts/v1/ProofManagerV1U1.sol | 7 +- contracts/v1/ServiceAgreementV1.sol | 48 +- contracts/v1/ShardingTable.sol | 4 +- contracts/v1/Staking.sol | 24 +- contracts/v1/assets/ContentAsset.sol | 29 +- .../v1/errors/ServiceAgreementErrorsV1U1.sol | 14 - .../storage/ServiceAgreementStorageProxy.sol | 210 ++---- contracts/v1/utils/ByteArr.sol | 4 +- contracts/v2/CommitManagerV1.sol | 208 +++--- contracts/v2/CommitManagerV1U1.sol | 683 ++++++++++++++++++ contracts/v2/ShardingTable.sol | 24 +- contracts/v2/Staking.sol | 67 +- contracts/v2/scoring/LinearSum.sol | 4 +- .../v2/storage/assets/ContentAssetStorage.sol | 4 +- 19 files changed, 964 insertions(+), 456 deletions(-) create mode 100644 contracts/v2/CommitManagerV1U1.sol diff --git a/contracts/v1/CommitManagerV1.sol b/contracts/v1/CommitManagerV1.sol index 6d70a68d..79904845 100644 --- a/contracts/v1/CommitManagerV1.sol +++ b/contracts/v1/CommitManagerV1.sol @@ -93,9 +93,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)); @@ -255,13 +253,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 +273,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..c1627f8b 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 { @@ -92,7 +93,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 +101,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 +111,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 +127,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 +151,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 +195,7 @@ contract CommitManagerV1U1 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; @@ -220,7 +219,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 +256,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 +267,7 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { ); if (!sasProxy.agreementV1U1Exists(agreementId)) - revert ServiceAgreementErrorsV1U1.ServiceAgreementDoesntExist(agreementId); + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); if (!reqs[2] && !isUpdateCommitWindowOpen(agreementId, args.epoch, unfinalizedStateIndex)) { uint256 commitWindowEnd = sasProxy.getUpdateCommitsDeadline( @@ -291,7 +289,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 +322,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 +405,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 +425,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..d41011a5 100644 --- a/contracts/v1/Profile.sol +++ b/contracts/v1/Profile.sol @@ -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 71582bab..dfb54c99 100644 --- a/contracts/v1/ServiceAgreementV1.sol +++ b/contracts/v1/ServiceAgreementV1.sol @@ -17,6 +17,7 @@ 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"; @@ -79,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, @@ -143,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); } @@ -164,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); } @@ -187,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 { @@ -211,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( @@ -249,7 +237,7 @@ 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; diff --git a/contracts/v1/ShardingTable.sol b/contracts/v1/ShardingTable.sol index 38266024..bcc56127 100644 --- a/contracts/v1/ShardingTable.sol +++ b/contracts/v1/ShardingTable.sol @@ -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(); 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/ServiceAgreementErrorsV1U1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol index 691fca84..e4764d8f 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1U1.sol @@ -3,20 +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 ServiceAgreementHasBeenExpired( - bytes32 agreementId, - uint256 startTime, - uint16 epochsNumber, - uint128 epochLength - ); error CommitWindowClosed( bytes32 agreementId, uint16 epoch, @@ -25,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/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/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 index f758b25d..851c984d 100644 --- a/contracts/v2/CommitManagerV1.sol +++ b/contracts/v2/CommitManagerV1.sol @@ -18,11 +18,11 @@ 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 {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; -import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( @@ -38,7 +38,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { string private constant _NAME = "CommitManagerV1"; string private constant _VERSION = "2.0.0"; - uint8 private constant LOG2PLDSF_ID = 1; + uint8 private constant _LOG2PLDSF_ID = 1; bool[4] public reqs = [false, false, false, false]; @@ -101,9 +101,7 @@ contract CommitManagerV2 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)); @@ -153,8 +151,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; - ShardingTableStorageV2 sts = shardingTableStorage; - ProfileStorage ps = profileStorage; bytes32 agreementId = hashingProxy.callHashFunction( args.hashFunctionId, @@ -166,7 +162,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); - if (proximityScoreFunctionPairId == LOG2PLDSF_ID) + if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( agreementId, args.epoch, @@ -190,7 +186,9 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint72 identityId = identityStorage.getIdentityId(msg.sender); - if (!reqs[1] && !sts.nodeExists(identityId)) { + if (!reqs[1] && !shardingTableStorage.nodeExists(identityId)) { + ProfileStorage ps = profileStorage; + revert ServiceAgreementErrorsV1.NodeNotInShardingTable( identityId, ps.getNodeId(identityId), @@ -199,9 +197,65 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); } - ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(args.closestNodeIndex); - ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(args.leftEdgeNodeIndex); - ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(args.rightEdgeNodeIndex); + (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( + agreementId, + args.epoch, + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + uint256 distance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + profileStorage.getNodeId(identityId) + ); + + uint40 score = proximityScoringProxy.callScoreFunction( + proximityScoreFunctionPairId, + 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 proximityScoreFunctionPairId, + uint8 hashFunctionId, + bytes calldata keyword, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex + ) internal virtual returns (uint72, uint256) { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + + ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(closestNodeIndex); + ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(leftEdgeNodeIndex); + ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(rightEdgeNodeIndex); // Verify that closestNode is in smaller arc between leftNode and rightNode bool isBetween = (leftEdgeNode.hashRingPosition <= rightEdgeNode.hashRingPosition) @@ -210,16 +264,15 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { : (leftEdgeNode.hashRingPosition <= closestNode.hashRingPosition || closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); - if (!isBetween) { + if (!isBetween) revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( agreementId, - args.epoch, - args.closestNodeIndex, - args.leftEdgeNodeIndex, - args.rightEdgeNodeIndex, + epoch, + closestNodeIndex, + leftEdgeNodeIndex, + rightEdgeNodeIndex, block.timestamp ); - } // Verify number of nodes between leftNode and rightNode (should be R2) uint72 nodesCount = sts.nodesCount(); @@ -232,24 +285,23 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ? nodesInBetweenClockwise + 2 : nodesCount - nodesInBetweenClockwise; - if (neighborhoodSize != parametersStorage.r2()) { + if (neighborhoodSize != parametersStorage.r2()) revert CommitManagerErrorsV2.InvalidNeighborhoodSize( agreementId, - args.epoch, - args.leftEdgeNodeIndex, - args.rightEdgeNodeIndex, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex, nodesCount, parametersStorage.r2(), neighborhoodSize, block.timestamp ); - } // Verify that closestNode is indeed closest uint256 closestDistance = proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(closestNode.identityId) ); @@ -257,44 +309,43 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { closestDistance > proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(closestNode.prevIdentityId) ) || closestDistance > proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(closestNode.nextIdentityId) ) - ) { + ) revert CommitManagerErrorsV2.InvalidClosestNode( agreementId, - args.epoch, - args.closestNodeIndex, + epoch, + closestNodeIndex, closestDistance, proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(closestNode.prevIdentityId) ), proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(closestNode.nextIdentityId) ), block.timestamp ); - } // Verify that leftNode is indeed the left edge of the Neighborhood uint256 leftEdgeDistance = proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(leftEdgeNode.identityId) ); @@ -302,31 +353,30 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { leftEdgeDistance > proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(rightEdgeNode.nextIdentityId) ) - ) { + ) revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, - args.epoch, - args.leftEdgeNodeIndex, + epoch, + leftEdgeNodeIndex, leftEdgeDistance, proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(rightEdgeNode.nextIdentityId) ), block.timestamp ); - } // Verify that rightNode is indeed the right edge of the Neighborhood uint256 rightEdgeDistance = proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(rightEdgeNode.identityId) ); @@ -334,57 +384,26 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { rightEdgeDistance > proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(leftEdgeNode.prevIdentityId) ) - ) { + ) revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, - args.epoch, - args.rightEdgeNodeIndex, + epoch, + rightEdgeNodeIndex, rightEdgeDistance, proximityScoringProxy.callProximityFunction( proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, + hashFunctionId, + keyword, ps.getNodeId(leftEdgeNode.prevIdentityId) ), block.timestamp ); - } - - uint256 distance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - args.hashFunctionId, - args.keyword, - ps.getNodeId(identityId) - ); - uint256 maxDistance = (leftEdgeDistance > rightEdgeDistance) ? leftEdgeDistance : rightEdgeDistance; - uint40 score = proximityScoringProxy.callScoreFunction( - proximityScoreFunctionPairId, - 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; + return (nodesCount, (leftEdgeDistance > rightEdgeDistance) ? leftEdgeDistance : rightEdgeDistance); } function _insertCommit( @@ -436,13 +455,13 @@ contract CommitManagerV2 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); @@ -456,11 +475,10 @@ contract CommitManagerV2 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/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol new file mode 100644 index 00000000..e069a1d5 --- /dev/null +++ b/contracts/v2/CommitManagerV1U1.sol @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.16; + +import {HashingProxy} from "../v1/HashingProxy.sol"; +import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; +import {StakingV2} from "./Staking.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 {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; +import {GeneralErrors} from "../v1/errors/GeneralErrors.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 indexed assetContract, + uint256 indexed tokenId, + bytes keyword, + uint8 hashFunctionId, + uint16 epoch, + uint256 stateIndex, + uint72 indexed identityId, + uint40 score + ); + event StateFinalized( + address indexed assetContract, + uint256 indexed 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; + + bool[6] public reqs = [false, false, false, false, false, false]; + + HashingProxy public hashingProxy; + ProximityScoringProxy public proximityScoringProxy; + StakingV2 public stakingContract; + 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 { + hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); + proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); + stakingContract = StakingV2(hub.getContractAddress("Staking")); + 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(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 agreementId = hashingProxy.callHashFunction( + args.hashFunctionId, + abi.encodePacked(args.assetContract, args.tokenId, args.keyword) + ); + + if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); + + if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + proximityScoreFunctionPairId, + 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, + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + uint256 distance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + profileStorage.getNodeId(identityId) + ); + + uint40 score = proximityScoringProxy.callScoreFunction( + proximityScoreFunctionPairId, + distance, + 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(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + UnfinalizedStateStorage uss = unfinalizedStateStorage; + AbstractAsset generalAssetInterface = AbstractAsset(args.assetContract); + + 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); + + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 agreementId = hashingProxy.callHashFunction( + args.hashFunctionId, + abi.encodePacked(args.assetContract, args.tokenId, args.keyword) + ); + + if (!sasProxy.agreementV1U1Exists(agreementId)) + revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); + + uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); + + if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) + revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( + agreementId, + args.epoch, + proximityScoreFunctionPairId, + 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, + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + args.closestNodeIndex, + args.leftEdgeNodeIndex, + args.rightEdgeNodeIndex + ); + + uint256 distance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + args.hashFunctionId, + args.keyword, + profileStorage.getNodeId(identityId) + ); + + uint40 score = proximityScoringProxy.callScoreFunction( + proximityScoreFunctionPairId, + distance, + 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 proximityScoreFunctionPairId, + uint8 hashFunctionId, + bytes calldata keyword, + uint72 closestNodeIndex, + uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeIndex + ) internal virtual returns (uint72, uint256) { + ShardingTableStorageV2 sts = shardingTableStorage; + ProfileStorage ps = profileStorage; + + ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(closestNodeIndex); + ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(leftEdgeNodeIndex); + ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(rightEdgeNodeIndex); + + // Verify that closestNode is in smaller arc between leftNode and rightNode + bool isBetween = (leftEdgeNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + ? (closestNode.hashRingPosition >= leftEdgeNode.hashRingPosition && + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition) + : (leftEdgeNode.hashRingPosition <= closestNode.hashRingPosition || + closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); + + if (!isBetween) + revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( + agreementId, + epoch, + closestNodeIndex, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + block.timestamp + ); + + // Verify number of nodes between leftNode and rightNode (should be R2) + uint72 nodesCount = sts.nodesCount(); + uint72 nodesInBetweenClockwise = ( + (rightEdgeNode.index > leftEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index - 1 + : leftEdgeNode.index - rightEdgeNode.index - 1 + ); + uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) + ? nodesInBetweenClockwise + 2 + : nodesCount - nodesInBetweenClockwise; + + if (neighborhoodSize != parametersStorage.r2()) + revert CommitManagerErrorsV2.InvalidNeighborhoodSize( + agreementId, + epoch, + leftEdgeNodeIndex, + rightEdgeNodeIndex, + nodesCount, + parametersStorage.r2(), + neighborhoodSize, + block.timestamp + ); + + // Verify that closestNode is indeed closest + uint256 closestDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(closestNode.identityId) + ); + + if ( + closestDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(closestNode.prevIdentityId) + ) || + closestDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(closestNode.nextIdentityId) + ) + ) + revert CommitManagerErrorsV2.InvalidClosestNode( + agreementId, + epoch, + closestNodeIndex, + closestDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(closestNode.prevIdentityId) + ), + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(closestNode.nextIdentityId) + ), + block.timestamp + ); + + // Verify that leftNode is indeed the left edge of the Neighborhood + uint256 leftEdgeDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(leftEdgeNode.identityId) + ); + + if ( + leftEdgeDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(rightEdgeNode.nextIdentityId) + ) + ) + revert CommitManagerErrorsV2.InvalidLeftEdgeNode( + agreementId, + epoch, + leftEdgeNodeIndex, + leftEdgeDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(rightEdgeNode.nextIdentityId) + ), + block.timestamp + ); + + // Verify that rightNode is indeed the right edge of the Neighborhood + uint256 rightEdgeDistance = proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(rightEdgeNode.identityId) + ); + + if ( + rightEdgeDistance > + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(leftEdgeNode.prevIdentityId) + ) + ) + revert CommitManagerErrorsV2.InvalidRightEdgeNode( + agreementId, + epoch, + rightEdgeNodeIndex, + rightEdgeDistance, + proximityScoringProxy.callProximityFunction( + proximityScoreFunctionPairId, + hashFunctionId, + keyword, + ps.getNodeId(leftEdgeNode.prevIdentityId) + ), + 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/ShardingTable.sol b/contracts/v2/ShardingTable.sol index f317eaaf..0a391a8e 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -130,27 +130,25 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory prevNode = sts.getNode(prevIdentityId); - if (prevNode.hashRingPosition > newNodeHashRingPosition) { + if (prevNode.hashRingPosition > newNodeHashRingPosition) revert ShardingTableErrors.InvalidPreviousIdentityId( identityId, newNodeHashRingPosition, prevIdentityId, prevNode.hashRingPosition ); - } ShardingTableStructsV2.Node memory nextNode = sts.getNode(nextIdentityId); - if (nextNode.identityId != NULL && nextNode.hashRingPosition < newNodeHashRingPosition) { + if (nextNode.identityId != NULL && nextNode.hashRingPosition < newNodeHashRingPosition) revert ShardingTableErrors.InvalidNextIdentityId( identityId, newNodeHashRingPosition, nextIdentityId, nextNode.hashRingPosition ); - } - if (prevNode.nextIdentityId != nextNode.prevIdentityId) { + if (prevNode.nextIdentityId != nextNode.prevIdentityId) revert ShardingTableErrors.InvalidPreviousOrNextIdentityId( identityId, prevIdentityId, @@ -158,21 +156,15 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { nextIdentityId, prevNode.nextIdentityId ); - } sts.createNodeObject(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId, nextNode.index); sts.setIdentityId(nextNode.index, identityId); sts.incrementNodesCount(); - if (prevIdentityId == NULL) { - sts.setHead(identityId); - } else { - sts.link(prevIdentityId, identityId); - } + if (prevIdentityId == NULL) sts.setHead(identityId); + else sts.link(prevIdentityId, identityId); - if (nextIdentityId != NULL) { - sts.link(identityId, nextIdentityId); - } + if (nextIdentityId != NULL) sts.link(identityId, nextIdentityId); uint72 index = nextNode.index + 1; while (nextIdentityId != NULL) { @@ -200,9 +192,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.NodeInfo[] memory nodesPage; ShardingTableStorageV2 sts = shardingTableStorage; - if ((sts.nodesCount() == 0) || (nodesNumber == 0)) { - return nodesPage; - } + if ((sts.nodesCount() == 0) || (nodesNumber == 0)) return nodesPage; ShardingTableStructsV2.Node memory startingNode = sts.getNode(startingIdentityId); diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index f9b1edfc..ff4b6634 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -15,9 +15,9 @@ 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 {TokenErrors} from "../v1/errors/TokenErrors.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"; @@ -96,22 +96,17 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { } function startStakeWithdrawal(uint72 identityId, uint96 sharesToBurn) external { - if (sharesToBurn == 0) { - revert StakingErrors.ZeroSharesAmount(); - } + if (sharesToBurn == 0) revert StakingErrors.ZeroSharesAmount(); ProfileStorage ps = profileStorage; StakingStorage ss = stakingStorage; - if (!ps.profileExists(identityId)) { - revert ProfileErrors.ProfileDoesntExist(identityId); - } + if (!ps.profileExists(identityId)) revert ProfileErrors.ProfileDoesntExist(identityId); Shares sharesContract = Shares(ps.getSharesContractAddress(identityId)); - if (sharesToBurn > sharesContract.balanceOf(msg.sender)) { + 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()); @@ -125,9 +120,8 @@ contract StakingV2 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, @@ -142,9 +136,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { function withdrawStake(uint72 identityId) external { ProfileStorage ps = profileStorage; - if (!ps.profileExists(identityId)) { - revert ProfileErrors.ProfileDoesntExist(identityId); - } + if (!ps.profileExists(identityId)) revert ProfileErrors.ProfileDoesntExist(identityId); StakingStorage ss = stakingStorage; @@ -152,12 +144,8 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint256 withdrawalTimestamp; (stakeWithdrawalAmount, withdrawalTimestamp) = ss.withdrawalRequests(identityId, msg.sender); - if (stakeWithdrawalAmount == 0) { - revert StakingErrors.WithdrawalWasntInitiated(); - } - if (withdrawalTimestamp >= block.timestamp) { - revert StakingErrors.WithdrawalPeriodPending(withdrawalTimestamp); - } + if (stakeWithdrawalAmount == 0) revert StakingErrors.WithdrawalWasntInitiated(); + if (withdrawalTimestamp >= block.timestamp) revert StakingErrors.WithdrawalPeriodPending(withdrawalTimestamp); ss.deleteWithdrawalRequest(identityId, msg.sender); ss.transferStake(msg.sender, stakeWithdrawalAmount); @@ -186,9 +174,8 @@ contract StakingV2 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.insertNode(identityId); - } } emit AccumulatedOperatorFeeIncreased( @@ -199,11 +186,8 @@ contract StakingV2 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); } @@ -214,9 +198,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { } function setOperatorFee(uint72 identityId, uint8 operatorFee) external onlyAdmin(identityId) { - if (operatorFee > 100) { - revert StakingErrors.InvalidOperatorFee(); - } + if (operatorFee > 100) revert StakingErrors.InvalidOperatorFee(); stakingStorage.setOperatorFee(identityId, operatorFee); emit OperatorFeeUpdated(identityId, profileStorage.getNodeId(identityId), operatorFee); @@ -231,39 +213,30 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint96 oldStake = ss.totalStakes(identityId); uint96 newStake = oldStake + stakeAmount; - if (!ps.profileExists(identityId)) { - revert ProfileErrors.ProfileDoesntExist(identityId); - } - if (stakeAmount > tknc.allowance(sender, address(this))) { + 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()); - } + 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); - } + 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()) { + if (!shardingTableStorage.nodeExists(identityId) && newStake >= params.minimumStake()) shardingTableContract.insertNode(identityId); - } emit StakeIncreased(identityId, ps.getNodeId(identityId), sender, oldStake, newStake); } function _checkAdmin(uint72 identityId) internal view virtual { - if (!identityStorage.keyHasPurpose(identityId, keccak256(abi.encodePacked(msg.sender)), ADMIN_KEY)) { + if (!identityStorage.keyHasPurpose(identityId, keccak256(abi.encodePacked(msg.sender)), ADMIN_KEY)) revert GeneralErrors.OnlyProfileAdminFunction(msg.sender); - } } } diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 4b7e47eb..21dabd21 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -77,9 +77,7 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende } function normalizeDistance(uint256 distance, uint256 maxDistance, uint72 nodesCount) public view returns (uint64) { - if (distance == 0) { - return 0; - } + if (distance == 0) return 0; uint256 idealMaxDistance = (HASH_RING_SIZE / nodesCount) * (parametersStorage.r2() / 2); uint256 divisor = (maxDistance <= idealMaxDistance) ? maxDistance : idealMaxDistance; 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) { From eb2126d841a1c803353574704bac617018193da5 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 10:21:15 +0100 Subject: [PATCH 22/46] Updated deployment scripts --- ... => 008_deploy_proximity_scoring_proxy.ts} | 7 ++-- deploy/031_deploy_commit_manager_v2.ts | 38 +++++++++++++++++++ ..._v1.ts => 032_deploy_commit_manager_v1.ts} | 10 +++++ ....ts => 033_deploy_commit_manager_v2_u1.ts} | 8 ++-- ....ts => 034_deploy_commit_manager_v1_u1.ts} | 0 ...r_v1.ts => 035_deploy_proof_manager_v1.ts} | 0 ...1.ts => 036_deploy_proof_manager_v1_u1.ts} | 0 ....ts => 037_deploy_service_agreement_v1.ts} | 0 ...t_asset.ts => 038_deploy_content_asset.ts} | 0 9 files changed, 56 insertions(+), 7 deletions(-) rename deploy/{008_deploy_scoring_proxy.ts => 008_deploy_proximity_scoring_proxy.ts} (60%) create mode 100644 deploy/031_deploy_commit_manager_v2.ts rename deploy/{031_deploy_commit_manager_v1.ts => 032_deploy_commit_manager_v1.ts} (68%) rename deploy/{032_deploy_commit_manager_v2.ts => 033_deploy_commit_manager_v2_u1.ts} (86%) rename deploy/{033_deploy_commit_manager_v1_u1.ts => 034_deploy_commit_manager_v1_u1.ts} (100%) rename deploy/{034_deploy_proof_manager_v1.ts => 035_deploy_proof_manager_v1.ts} (100%) rename deploy/{035_deploy_proof_manager_v1_u1.ts => 036_deploy_proof_manager_v1_u1.ts} (100%) rename deploy/{036_deploy_service_agreement_v1.ts => 037_deploy_service_agreement_v1.ts} (100%) rename deploy/{037_deploy_content_asset.ts => 038_deploy_content_asset.ts} (100%) diff --git a/deploy/008_deploy_scoring_proxy.ts b/deploy/008_deploy_proximity_scoring_proxy.ts similarity index 60% rename from deploy/008_deploy_scoring_proxy.ts rename to deploy/008_deploy_proximity_scoring_proxy.ts index 6940462a..75379361 100644 --- a/deploy/008_deploy_scoring_proxy.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: 'ScoringProxy', + newContractName: 'ProximityScoringProxy', + newContractNameInHub: 'ScoringProxy', }); }; export default func; -func.tags = ['ScoringProxy', 'v1']; -func.dependencies = ['Hub']; +func.tags = ['ProximityScoringProxy', 'v2']; +func.dependencies = ['HubV2']; diff --git a/deploy/031_deploy_commit_manager_v2.ts b/deploy/031_deploy_commit_manager_v2.ts new file mode 100644 index 00000000..ccdb120a --- /dev/null +++ b/deploy/031_deploy_commit_manager_v2.ts @@ -0,0 +1,38 @@ +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 CommitManagerV1 = await hre.helpers.deploy({ + newContractName: 'CommitManagerV2', + newContractNameInHub: 'CommitManagerV1', + }); + + await hre.helpers.updateContractParameters('CommitManagerV1', CommitManagerV1); +}; + +export default func; +func.tags = ['CommitManagerV2', 'v2']; +func.dependencies = [ + 'Hub', + 'IdentityStorageV2', + 'ProximityScoringProxy', + 'Log2PLDSF', + 'ParametersStorage', + 'ProfileStorage', + 'ServiceAgreementStorageProxy', + 'HashingProxy', + 'SHA256', + 'ShardingTableStorageV2', + 'StakingV2', + 'StakingStorage', +]; diff --git a/deploy/031_deploy_commit_manager_v1.ts b/deploy/032_deploy_commit_manager_v1.ts similarity index 68% rename from deploy/031_deploy_commit_manager_v1.ts rename to deploy/032_deploy_commit_manager_v1.ts index e9d34d46..98714077 100644 --- a/deploy/031_deploy_commit_manager_v1.ts +++ b/deploy/032_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/032_deploy_commit_manager_v2.ts b/deploy/033_deploy_commit_manager_v2_u1.ts similarity index 86% rename from deploy/032_deploy_commit_manager_v2.ts rename to deploy/033_deploy_commit_manager_v2_u1.ts index a159d18c..b2cb6f47 100644 --- a/deploy/032_deploy_commit_manager_v2.ts +++ b/deploy/033_deploy_commit_manager_v2_u1.ts @@ -10,10 +10,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { return; } - console.log('Deploying CommitManager V2...'); + console.log('Deploying CommitManager V2U1...'); const CommitManagerV1U1 = await hre.helpers.deploy({ - newContractName: 'CommitManagerV2', + newContractName: 'CommitManagerV2U1', newContractNameInHub: 'CommitManagerV1U1', }); @@ -21,12 +21,12 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { }; export default func; -func.tags = ['CommitManagerV2', 'v2']; +func.tags = ['CommitManagerV2U1', 'v2']; func.dependencies = [ 'ContentAssetStorage', 'HubV2', 'IdentityStorageV2', - 'ScoringProxy', + 'ProximityScoringProxy', 'Log2PLDSF', 'ParametersStorage', 'ProfileStorage', diff --git a/deploy/033_deploy_commit_manager_v1_u1.ts b/deploy/034_deploy_commit_manager_v1_u1.ts similarity index 100% rename from deploy/033_deploy_commit_manager_v1_u1.ts rename to deploy/034_deploy_commit_manager_v1_u1.ts diff --git a/deploy/034_deploy_proof_manager_v1.ts b/deploy/035_deploy_proof_manager_v1.ts similarity index 100% rename from deploy/034_deploy_proof_manager_v1.ts rename to deploy/035_deploy_proof_manager_v1.ts diff --git a/deploy/035_deploy_proof_manager_v1_u1.ts b/deploy/036_deploy_proof_manager_v1_u1.ts similarity index 100% rename from deploy/035_deploy_proof_manager_v1_u1.ts rename to deploy/036_deploy_proof_manager_v1_u1.ts diff --git a/deploy/036_deploy_service_agreement_v1.ts b/deploy/037_deploy_service_agreement_v1.ts similarity index 100% rename from deploy/036_deploy_service_agreement_v1.ts rename to deploy/037_deploy_service_agreement_v1.ts diff --git a/deploy/037_deploy_content_asset.ts b/deploy/038_deploy_content_asset.ts similarity index 100% rename from deploy/037_deploy_content_asset.ts rename to deploy/038_deploy_content_asset.ts From c8678ae8ce68d0f33241592ad4009ba3fdf22a0d Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 10:34:59 +0100 Subject: [PATCH 23/46] Generated new ABIs, updated package exports --- abi/CommitManagerV2.json | 785 ++++++++++++++++++++ abi/CommitManagerV2U1.json | 986 ++++++++++++++++++++++++++ abi/IProximityScoreFunctionsPair.json | 65 ++ abi/LinearSum.json | 342 +++++++++ abi/ProximityScoringProxy.json | 331 +++++++++ abi/ServiceAgreementV1.json | 10 + abi/ShardingTable.json | 4 +- abi/ShardingTableStorage.json | 4 +- abi/ShardingTableStorageV2.json | 430 +++++++++++ abi/ShardingTableV2.json | 397 +++++++++++ abi/StakingV2.json | 573 +++++++++++++++ hardhat.config.ts | 6 + index.ts | 35 +- 13 files changed, 3953 insertions(+), 15 deletions(-) create mode 100644 abi/CommitManagerV2.json create mode 100644 abi/CommitManagerV2U1.json create mode 100644 abi/IProximityScoreFunctionsPair.json create mode 100644 abi/LinearSum.json create mode 100644 abi/ProximityScoringProxy.json create mode 100644 abi/ShardingTableStorageV2.json create mode 100644 abi/ShardingTableV2.json create mode 100644 abi/StakingV2.json diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json new file mode 100644 index 00000000..c5583e37 --- /dev/null +++ b/abi/CommitManagerV2.json @@ -0,0 +1,785 @@ +[ + { + "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": "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": "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": "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": "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": "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": "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": [], + "name": "proximityScoringProxy", + "outputs": [ + { + "internalType": "contract ProximityScoringProxy", + "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": "stakingContract", + "outputs": [ + { + "internalType": "contract StakingV2", + "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": "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..b47b2591 --- /dev/null +++ b/abi/CommitManagerV2U1.json @@ -0,0 +1,986 @@ +[ + { + "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": "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": "uint256", + "name": "rightEdgeNodeDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leftEdgeNodeAdjacentDistance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeNow", + "type": "uint256" + } + ], + "name": "InvalidRightEdgeNode", + "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": 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": false, + "internalType": "uint256", + "name": "stateIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "indexed": false, + "internalType": "uint40", + "name": "score", + "type": "uint40" + } + ], + "name": "CommitSubmitted", + "type": "event" + }, + { + "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": 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": "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": "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": "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": [], + "name": "proximityScoringProxy", + "outputs": [ + { + "internalType": "contract ProximityScoringProxy", + "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": "stakingContract", + "outputs": [ + { + "internalType": "contract StakingV2", + "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": "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": "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..2bb7f7dd --- /dev/null +++ b/abi/LinearSum.json @@ -0,0 +1,342 @@ +[ + { + "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": "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": "uint192", + "name": "", + "type": "uint192" + }, + { + "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/ProximityScoringProxy.json b/abi/ProximityScoringProxy.json new file mode 100644 index 00000000..17fb63e7 --- /dev/null +++ b/abi/ProximityScoringProxy.json @@ -0,0 +1,331 @@ +[ + { + "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": "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": [ + { + "internalType": "uint8", + "name": "scoreFunctionId", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "distance", + "type": "uint256" + }, + { + "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..235632d0 --- /dev/null +++ b/abi/ShardingTableStorageV2.json @@ -0,0 +1,430 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "hubAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "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": "getIdentityIdByIndex", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "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": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "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": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "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": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "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": "leftNodeIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightNodeIdentityId", + "type": "uint72" + } + ], + "name": "link", + "outputs": [], + "stateMutability": "nonpayable", + "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": [], + "name": "nodesCount", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "setHead", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "setIdentityId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "newNextIdentityId", + "type": "uint72" + } + ], + "name": "setNextIdentityId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "newPrevIdentityId", + "type": "uint72" + } + ], + "name": "setPrevIdentityId", + "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..8b1a1400 --- /dev/null +++ b/abi/ShardingTableV2.json @@ -0,0 +1,397 @@ +[ + { + "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": "uint72", + "name": "nextIdentityId", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "nextHashRingPosition", + "type": "uint256" + } + ], + "name": "InvalidNextIdentityId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint256", + "name": "prevHashRingPosition", + "type": "uint256" + } + ], + "name": "InvalidPreviousIdentityId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "sentPrevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "realPrevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "sentNextIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "realNextIdentityId", + "type": "uint72" + } + ], + "name": "InvalidPreviousOrNextIdentityId", + "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": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "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 ShardingTableStructsV2.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": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "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 ShardingTableStructsV2.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": "identityId", + "type": "uint72" + } + ], + "name": "insertNode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "prevIdentityId", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "nextIdentityId", + "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..ce859ff9 --- /dev/null +++ b/abi/StakingV2.json @@ -0,0 +1,573 @@ +[ + { + "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": "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": "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": "OperatorFeeUpdated", + "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": [], + "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": "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": "uint72", + "name": "identityId", + "type": "uint72" + }, + { + "internalType": "uint8", + "name": "operatorFee", + "type": "uint8" + } + ], + "name": "setOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "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": "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/hardhat.config.ts b/hardhat.config.ts index 826fe7c2..be9d7d92 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -122,9 +122,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/index.ts b/index.ts index 70660d08..8bb7008e 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,7 +22,8 @@ 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'; @@ -29,7 +32,7 @@ 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 +40,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 +55,7 @@ const ABIV1 = { HubController, ParametersStorage, HashingProxy, - ScoringProxy, + ScoringProxy: ProximityScoringProxy, SHA256, ShardingTableStorage, ShardingTable, @@ -68,6 +74,7 @@ const ABIV1 = { Token, IdentityStorage, Identity, + LinearSum, Log2PLDSF, ProfileStorage, Profile, @@ -84,7 +91,7 @@ const ABIV1 = { IHashFunction, Indexable, Initializable, - IScoreFunction, + IScoreFunction: IProximityScoreFunctionsPair, Named, Versioned, }; @@ -93,6 +100,11 @@ const ABIV2 = { HubV2, ContentAssetStorageV2, IdentityStorageV2, + CommitManagerV2, + CommitManagerV2U1, + ShardingTableV2, + ShardingTableStorageV2, + StakingV2, }; export { @@ -100,18 +112,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 +131,12 @@ export { Token as TokenABI, IdentityStorageV2 as IdentityStorageABI, Identity as IdentityABI, + LinearSum as LinearSumABI, Log2PLDSF as Log2PLDSFABI, 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 +148,7 @@ export { IHashFunction as IHashFunctionABI, Indexable as IndexableABI, Initializable as InitializableABI, - IScoreFunction as IScoreFunctionABI, + IProximityScoreFunctionsPair as IScoreFunctionABI, Named as NamedABI, Versioned as VersionedABI, ABIV1, From 2d932a7bc91368a305235454204c6394eaa68803 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 11:59:45 +0100 Subject: [PATCH 24/46] Fixed ProximityScoringProxy, added missing parameters/checks --- abi/CommitManagerV1.json | 26 +++++++++++++++++ abi/CommitManagerV1U1.json | 26 +++++++++++++++++ abi/ProximityScoringProxy.json | 28 +++++++++++++------ contracts/v1/CommitManagerV1.sol | 12 +++++++- contracts/v1/CommitManagerV1U1.sol | 20 ++++++++++++- .../v1/errors/ServiceAgreementErrorsV1.sol | 1 + contracts/v2/ProximityScoringProxy.sol | 15 +++++++--- contracts/v2/Staking.sol | 2 +- contracts/v2/scoring/LinearSum.sol | 3 +- deploy/008_deploy_proximity_scoring_proxy.ts | 2 +- deployments/parameters.json | 24 ++++++++++++++++ test/v1/unit/CommitManagerV1.test.ts | 4 +-- test/v1/unit/CommitManagerV1U1.test.ts | 4 +-- test/v2/unit/ContentAssetStorageV2.test.ts | 13 ++++++++- 14 files changed, 157 insertions(+), 23 deletions(-) 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/ProximityScoringProxy.json b/abi/ProximityScoringProxy.json index 17fb63e7..b71d3c2d 100644 --- a/abi/ProximityScoringProxy.json +++ b/abi/ProximityScoringProxy.json @@ -90,19 +90,19 @@ "type": "uint8" }, { - "internalType": "uint256", - "name": "distance", - "type": "uint256" + "internalType": "uint8", + "name": "hashFunctionId", + "type": "uint8" }, { - "internalType": "uint256", - "name": "maxDistance", - "type": "uint256" + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" }, { - "internalType": "uint72", - "name": "nodesCount", - "type": "uint72" + "internalType": "bytes", + "name": "keyword", + "type": "bytes" }, { "internalType": "uint96", @@ -133,6 +133,16 @@ "name": "distance", "type": "uint256" }, + { + "internalType": "uint256", + "name": "maxDistance", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "nodesCount", + "type": "uint72" + }, { "internalType": "uint96", "name": "stake", diff --git a/contracts/v1/CommitManagerV1.sol b/contracts/v1/CommitManagerV1.sol index 79904845..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]; @@ -152,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); diff --git a/contracts/v1/CommitManagerV1U1.sol b/contracts/v1/CommitManagerV1U1.sol index c1627f8b..0d81e6aa 100644 --- a/contracts/v1/CommitManagerV1U1.sol +++ b/contracts/v1/CommitManagerV1U1.sol @@ -46,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]; @@ -197,6 +199,14 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { 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)) { @@ -269,6 +279,14 @@ contract CommitManagerV1U1 is Named, Versioned, ContractStatus, Initializable { 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)) diff --git a/contracts/v1/errors/ServiceAgreementErrorsV1.sol b/contracts/v1/errors/ServiceAgreementErrorsV1.sol index e35e2f3c..1b215967 100644 --- a/contracts/v1/errors/ServiceAgreementErrorsV1.sol +++ b/contracts/v1/errors/ServiceAgreementErrorsV1.sol @@ -24,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/v2/ProximityScoringProxy.sol b/contracts/v2/ProximityScoringProxy.sol index 68bb9e14..883c3a19 100644 --- a/contracts/v2/ProximityScoringProxy.sol +++ b/contracts/v2/ProximityScoringProxy.sol @@ -16,7 +16,7 @@ contract ProximityScoringProxy is Named, Versioned, ContractStatus { event ScoringFunctionContractUpdated(uint8 indexed scoreFunctionId, address newContractAddress); string private constant _NAME = "ScoringProxy"; - string private constant _VERSION = "1.0.1"; + string private constant _VERSION = "2.0.0"; UnorderedIndexableContractDynamicSetLib.Set internal scoreFunctionSet; @@ -45,9 +45,16 @@ contract ProximityScoringProxy is Named, Versioned, ContractStatus { scoreFunctionSet.remove(scoreFunctionId); } - function callScoreFunction(uint8 scoreFunctionId, uint256 distance, uint96 stake) external view returns (uint40) { - IScoreFunction scoreFunction = IScoreFunction(scoreFunctionSet.get(scoreFunctionId).addr); - return scoreFunction.calculateScore(distance, stake); + 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( diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index ff4b6634..1bf57e1e 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -47,7 +47,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { event OperatorFeeUpdated(uint72 indexed identityId, bytes nodeId, uint8 operatorFee); string private constant _NAME = "Staking"; - string private constant _VERSION = "1.0.2"; + string private constant _VERSION = "2.0.0"; ShardingTableV2 public shardingTableContract; IdentityStorageV2 public identityStorage; diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 21dabd21..114408f7 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -28,6 +28,7 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende constructor(address hubAddress) HubDependent(hubAddress) { distanceScaleFactor = 1e18; + stakeScaleFactor = 1e18; w1 = 1; w2 = 1; } @@ -104,7 +105,7 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint96 minStake = ps.minimumStake(); uint96 maxStake = ps.maximumStake(); - return uint64((uint256(distanceScaleFactor) * (stake - minStake)) / (maxStake - minStake)); + return uint64((uint256(stakeScaleFactor) * (stake - minStake)) / (maxStake - minStake)); } function getParameters() external view returns (uint192, uint32, uint32) { diff --git a/deploy/008_deploy_proximity_scoring_proxy.ts b/deploy/008_deploy_proximity_scoring_proxy.ts index 75379361..50e3c474 100644 --- a/deploy/008_deploy_proximity_scoring_proxy.ts +++ b/deploy/008_deploy_proximity_scoring_proxy.ts @@ -9,5 +9,5 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { }; export default func; -func.tags = ['ProximityScoringProxy', 'v2']; +func.tags = ['ScoringProxy', 'v2']; func.dependencies = ['HubV2']; diff --git a/deployments/parameters.json b/deployments/parameters.json index 84c48e8d..7ddad19d 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", @@ -224,6 +230,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -380,6 +392,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", @@ -536,6 +554,12 @@ } ] }, + "LinearSum": { + "w1": "1", + "w2": "1", + "distanceScaleFactor": "1000000000000000000", + "stakeScaleFactor": "1000000000000000000" + }, "Log2PLDSF": { "a": "1", "b": "0", 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/v2/unit/ContentAssetStorageV2.test.ts b/test/v2/unit/ContentAssetStorageV2.test.ts index 92ea1b94..f76df005 100644 --- a/test/v2/unit/ContentAssetStorageV2.test.ts +++ b/test/v2/unit/ContentAssetStorageV2.test.ts @@ -39,7 +39,18 @@ describe('@v2 @unit ContentAssetStorageV2', function () { }; async function deployContentAssetStorageV2Fixture(): Promise { - await hre.deployments.fixture(['HubV2', 'Token', 'ContentAssetStorageV2', 'IdentityStorageV2', 'ContentAsset']); + await hre.deployments.fixture([ + 'HubV2', + 'Token', + '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'); From 5bf3a15ead841ee330a0411f56064a9143a25fc9 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 25 Jan 2024 20:55:34 +0100 Subject: [PATCH 25/46] Added unit tests for ShardingTableV2, fixed insert/removal, added comments for the code --- contracts/v2/ShardingTable.sol | 52 ++++- contracts/v2/storage/ShardingTableStorage.sol | 5 +- test/v1/unit/ShardingTableStorage.test.ts | 5 +- test/v2/unit/ShardingTableV2.test.ts | 214 ++++++++++++++++++ 4 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 test/v2/unit/ShardingTableV2.test.ts diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index 0a391a8e..bbef6660 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -70,8 +70,13 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory nodeToRemove = sts.getNode(identityId); + // If removing Head => set new Head (can also be 0 if there is only 1 node in the list) + if (nodeToRemove.prevIdentityId == NULL) sts.setHead(nodeToRemove.nextIdentityId); + + // Link left and right nodes (both can be NULL, there is a check in link function) sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); + // Decrement indexes of all nodes after removed one + add pointers to identityId for changed indexes uint72 index = nodeToRemove.index; uint72 nextIdentityId = nodeToRemove.nextIdentityId; while (nextIdentityId != NULL) { @@ -84,7 +89,9 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; } + // Delete node object + set last index pointer to be NULL + decrement total nodes count sts.deleteNodeObject(identityId); + sts.setIdentityId(index, 0); sts.decrementNodesCount(); emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); @@ -104,12 +111,15 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory currentNode = sts.getNodeByIndex(uint72(mid)); if (currentNode.hashRingPosition < hashRingPosition) { + // Node is in the right half of the range, move left pointers prevIdentityId = currentNode.identityId; left = mid + 1; } else if (currentNode.hashRingPosition > hashRingPosition) { + // Node is in the left half of the range, move right pointers nextIdentityId = currentNode.identityId; right = mid - 1; } else { + // Exact match found prevIdentityId = currentNode.identityId; nextIdentityId = currentNode.nextIdentityId; break; @@ -130,7 +140,9 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory prevNode = sts.getNode(prevIdentityId); - if (prevNode.hashRingPosition > newNodeHashRingPosition) + // 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 < prevNode.hashRingPosition) revert ShardingTableErrors.InvalidPreviousIdentityId( identityId, newNodeHashRingPosition, @@ -140,7 +152,9 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory nextNode = sts.getNode(nextIdentityId); - if (nextNode.identityId != NULL && nextNode.hashRingPosition < newNodeHashRingPosition) + // 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.InvalidNextIdentityId( identityId, newNodeHashRingPosition, @@ -148,7 +162,11 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { nextNode.hashRingPosition ); - if (prevNode.nextIdentityId != nextNode.prevIdentityId) + // Verify that prevNode and nextNode are direct neighbors before inserting a new node between them + if ( + (prevIdentityId != NULL && nextIdentityId != prevNode.nextIdentityId) || + (nextIdentityId != NULL && prevIdentityId != nextNode.prevIdentityId) + ) revert ShardingTableErrors.InvalidPreviousOrNextIdentityId( identityId, prevIdentityId, @@ -157,16 +175,38 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { prevNode.nextIdentityId ); - sts.createNodeObject(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId, nextNode.index); - sts.setIdentityId(nextNode.index, identityId); + uint72 index; + if (nextIdentityId == NULL) { + // Inserting a new Tail + if (prevIdentityId != NULL) { + // The list is not empty, calculate the new Tail's index + index = prevNode.index + 1; + } else { + // The list is empty, start with index 0 + index = 0; + } + } else { + // Inserting a node before the nextNode + index = nextNode.index; + } + + // Create node object + set index pointer to new identityId + increment total nodes count + sts.createNodeObject(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId, index); + sts.setIdentityId(index, identityId); sts.incrementNodesCount(); + // If Head => add Head pointer + // If not Head => add the link between prevNode and new Node if (prevIdentityId == NULL) sts.setHead(identityId); else sts.link(prevIdentityId, identityId); + // If not Tail => add the link between new Node and nextNode if (nextIdentityId != NULL) sts.link(identityId, nextIdentityId); - uint72 index = nextNode.index + 1; + // Increment indexes of all nodes after inserted one + add pointers to identityId for changed indexes + unchecked { + index += 1; + } while (nextIdentityId != NULL) { sts.incrementNodeIndex(nextIdentityId); sts.setIdentityId(index, nextIdentityId); diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 6291cb38..78b2b414 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -119,7 +119,8 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { } function link(uint72 leftNodeIdentityId, uint72 rightNodeIdentityId) external onlyContracts { - nodes[leftNodeIdentityId].nextIdentityId = rightNodeIdentityId; - nodes[rightNodeIdentityId].prevIdentityId = leftNodeIdentityId; + if (leftNodeIdentityId != NULL) nodes[leftNodeIdentityId].nextIdentityId = rightNodeIdentityId; + + if (rightNodeIdentityId != NULL) nodes[rightNodeIdentityId].prevIdentityId = leftNodeIdentityId; } } diff --git a/test/v1/unit/ShardingTableStorage.test.ts b/test/v1/unit/ShardingTableStorage.test.ts index c6fe2acc..1ab45165 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'); @@ -56,6 +56,7 @@ describe('@v1 @unit ShardingTableStorage Contract', function () { const receipt = await singleIdentityId.wait(); identityId = receipt.events?.[3].args?.identityId.toNumber(); + console.log(identityId); idsArray.push(identityId); } return idsArray; diff --git a/test/v2/unit/ShardingTableV2.test.ts b/test/v2/unit/ShardingTableV2.test.ts new file mode 100644 index 00000000..82f03688 --- /dev/null +++ b/test/v2/unit/ShardingTableV2.test.ts @@ -0,0 +1,214 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import hre from 'hardhat'; + +import { HubController, Profile, ShardingTableStorageV2, ShardingTableV2 } from '../../../typechain'; + +type ShardingTableFixture = { + accounts: SignerWithAddress[]; + Profile: Profile; + ShardingTableStorage: ShardingTableStorageV2; + ShardingTable: ShardingTableV2; +}; + +describe('@v2 @unit ShardingTableV2 contract', function () { + let accounts: SignerWithAddress[]; + let Profile: Profile; + let ShardingTableStorage: ShardingTableStorageV2; + let ShardingTable: ShardingTableV2; + let identityId: number; + + const nodeId1 = '0x01'; + const nodeId2 = '0x02'; + const nodeId3 = '0x03'; + const nodeId4 = '0x04'; + const nodeId5 = '0x05'; + + // 3 1 2 4 5 + + // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId1)).toString()); + // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId2)).toString()); + // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId3)).toString()); + // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId4)).toString()); + // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId5)).toString()); + + 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'); + await HubController.setContractAddress('HubOwner', accounts[0].address); + + return { accounts, Profile, ShardingTableStorage, ShardingTable }; + } + + 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, 'Invalid number of nodes').to.equal(nodesCount); + + 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(), 'Invalid node on this position').to.equal(identityIds[i]); + + expect(node.index.toNumber(), 'Invalid node index').to.equal(i); + if (i === 0) { + expect(node.prevIdentityId.toNumber(), 'Invalid prevIdentityId').to.equal(0); + } else { + expect(node.prevIdentityId.toNumber(), 'Invalid prevIdentityId').to.equal(identityIds[i - 1]); + } + + if (i === nodesCount - 1) { + expect(node.nextIdentityId.toNumber(), 'Invalid nextIdentityId').to.equal(0); + } else { + expect(node.nextIdentityId.toNumber(), 'Invalid nextIdentityId').to.equal(identityIds[i + 1]); + } + } + } + + 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,uint72)'](profiles[1], 0, 0); + await validateShardingTableResult([2]); + + // 3 2 + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[2], 0, profiles[1]); + await validateShardingTableResult([3, 2]); + + // 3 2 5 + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[4], profiles[1], 0); + await validateShardingTableResult([3, 2, 5]); + + // 3 2 4 5 + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], profiles[4]); + await validateShardingTableResult([3, 2, 4, 5]); + + // 3 1 2 4 5 + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], profiles[1]); + 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 node with invalid prevIdentityId expect to fail', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], 0, 0); + + await expect( + ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[1], 0), + ).to.be.revertedWithCustomError(ShardingTable, 'InvalidPreviousIdentityId'); + }); + + it('Insert node with invalid nextIdentityId expect to fail', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], 0, 0); + + await expect( + ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], 0, profiles[1]), + ).to.be.revertedWithCustomError(ShardingTable, 'InvalidNextIdentityId'); + }); + + it('Insert node with invalid prevIdentityId and nextIdentityId expect to fail', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[2], 0, 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], profiles[2], 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], 0); + + await expect( + ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], profiles[3]), + ).to.be.revertedWithCustomError(ShardingTable, 'InvalidPreviousOrNextIdentityId'); + }); + + it('Remove node from sharding table, expect to pass', async () => { + const profiles = await createMultipleProfiles(); + + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[2], 0, 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], profiles[0], 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], 0); + await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[4], profiles[3], 0); + + // 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]); + }); +}); From add3d339ec89d92526cc96bef33703c5170e34f6 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 26 Jan 2024 12:00:16 +0100 Subject: [PATCH 26/46] Separated CommitManagerV2/V2U1, reworked deployment scripts, updated Staking parameters, added parameters for new Commit Managers --- contracts/v1/storage/ParametersStorage.sol | 6 +- ...ommitManagerV1.sol => CommitManagerV2.sol} | 0 ...tManagerV1U1.sol => CommitManagerV2U1.sol} | 0 ..._v1.ts => 031_deploy_commit_manager_v1.ts} | 6 +- ....ts => 032_deploy_commit_manager_v1_u1.ts} | 6 +- ..._v2.ts => 033_deploy_commit_manager_v2.ts} | 13 +- ....ts => 034_deploy_commit_manager_v2_u1.ts} | 13 +- deployments/parameters.json | 288 +++++++++++++++++- index.ts | 6 +- test/v1/unit/Staking.test.ts | 14 +- test/v2/unit/ContentAssetStorageV2.test.ts | 2 + 11 files changed, 302 insertions(+), 52 deletions(-) rename contracts/v2/{CommitManagerV1.sol => CommitManagerV2.sol} (100%) rename contracts/v2/{CommitManagerV1U1.sol => CommitManagerV2U1.sol} (100%) rename deploy/{032_deploy_commit_manager_v1.ts => 031_deploy_commit_manager_v1.ts} (75%) rename deploy/{034_deploy_commit_manager_v1_u1.ts => 032_deploy_commit_manager_v1_u1.ts} (76%) rename deploy/{031_deploy_commit_manager_v2.ts => 033_deploy_commit_manager_v2.ts} (58%) rename deploy/{033_deploy_commit_manager_v2_u1.ts => 034_deploy_commit_manager_v2_u1.ts} (60%) diff --git a/contracts/v1/storage/ParametersStorage.sol b/contracts/v1/storage/ParametersStorage.sol index 8eca8643..dee76b96 100644 --- a/contracts/v1/storage/ParametersStorage.sol +++ b/contracts/v1/storage/ParametersStorage.sol @@ -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/v2/CommitManagerV1.sol b/contracts/v2/CommitManagerV2.sol similarity index 100% rename from contracts/v2/CommitManagerV1.sol rename to contracts/v2/CommitManagerV2.sol diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV2U1.sol similarity index 100% rename from contracts/v2/CommitManagerV1U1.sol rename to contracts/v2/CommitManagerV2U1.sol diff --git a/deploy/032_deploy_commit_manager_v1.ts b/deploy/031_deploy_commit_manager_v1.ts similarity index 75% rename from deploy/032_deploy_commit_manager_v1.ts rename to deploy/031_deploy_commit_manager_v1.ts index 98714077..b47a6be9 100644 --- a/deploy/032_deploy_commit_manager_v1.ts +++ b/deploy/031_deploy_commit_manager_v1.ts @@ -2,11 +2,7 @@ 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.')) - ) { + if (hre.helpers.isDeployed('CommitManagerV2')) { return; } diff --git a/deploy/034_deploy_commit_manager_v1_u1.ts b/deploy/032_deploy_commit_manager_v1_u1.ts similarity index 76% rename from deploy/034_deploy_commit_manager_v1_u1.ts rename to deploy/032_deploy_commit_manager_v1_u1.ts index a5c4e8af..af113054 100644 --- a/deploy/034_deploy_commit_manager_v1_u1.ts +++ b/deploy/032_deploy_commit_manager_v1_u1.ts @@ -2,11 +2,7 @@ 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.')) - ) { + if (hre.helpers.isDeployed('CommitManagerV2U1')) { return; } diff --git a/deploy/031_deploy_commit_manager_v2.ts b/deploy/033_deploy_commit_manager_v2.ts similarity index 58% rename from deploy/031_deploy_commit_manager_v2.ts rename to deploy/033_deploy_commit_manager_v2.ts index ccdb120a..4891bd5d 100644 --- a/deploy/031_deploy_commit_manager_v2.ts +++ b/deploy/033_deploy_commit_manager_v2.ts @@ -2,22 +2,13 @@ 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 CommitManagerV1 = await hre.helpers.deploy({ + const CommitManagerV2 = await hre.helpers.deploy({ newContractName: 'CommitManagerV2', - newContractNameInHub: 'CommitManagerV1', }); - await hre.helpers.updateContractParameters('CommitManagerV1', CommitManagerV1); + await hre.helpers.updateContractParameters('CommitManagerV2', CommitManagerV2); }; export default func; diff --git a/deploy/033_deploy_commit_manager_v2_u1.ts b/deploy/034_deploy_commit_manager_v2_u1.ts similarity index 60% rename from deploy/033_deploy_commit_manager_v2_u1.ts rename to deploy/034_deploy_commit_manager_v2_u1.ts index b2cb6f47..5bb9a7eb 100644 --- a/deploy/033_deploy_commit_manager_v2_u1.ts +++ b/deploy/034_deploy_commit_manager_v2_u1.ts @@ -2,22 +2,13 @@ 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 CommitManagerV1U1 = await hre.helpers.deploy({ + const CommitManagerV2U1 = await hre.helpers.deploy({ newContractName: 'CommitManagerV2U1', - newContractNameInHub: 'CommitManagerV1U1', }); - await hre.helpers.updateContractParameters('CommitManagerV1U1', CommitManagerV1U1); + await hre.helpers.updateContractParameters('CommitManagerV2U1', CommitManagerV2U1); }; export default func; diff --git a/deployments/parameters.json b/deployments/parameters.json index 7ddad19d..907a6818 100644 --- a/deployments/parameters.json +++ b/deployments/parameters.json @@ -68,6 +68,74 @@ } ] }, + "CommitManagerV2": { + "reqs": [ + { + "desiredValue": false, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, false] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + } + ] + }, + "CommitManagerV2U1": { + "reqs": [ + { + "desiredValue": false, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, false] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + }, + { + "desiredValue": false, + "getterArgs": [4], + "setter": "setReq", + "setterArgs": [4, false] + }, + { + "desiredValue": false, + "getterArgs": [5], + "setter": "setReq", + "setterArgs": [5, false] + } + ] + }, "LinearSum": { "w1": "1", "w2": "1", @@ -91,7 +159,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -230,6 +298,74 @@ } ] }, + "CommitManagerV2": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + } + ] + }, + "CommitManagerV2U1": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": true, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, true] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + }, + { + "desiredValue": false, + "getterArgs": [4], + "setter": "setReq", + "setterArgs": [4, false] + }, + { + "desiredValue": false, + "getterArgs": [5], + "setter": "setReq", + "setterArgs": [5, false] + } + ] + }, "LinearSum": { "w1": "1", "w2": "1", @@ -253,7 +389,7 @@ "epochLength": "3600", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -392,6 +528,74 @@ } ] }, + "CommitManagerV2": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + } + ] + }, + "CommitManagerV2U1": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + }, + { + "desiredValue": false, + "getterArgs": [4], + "setter": "setReq", + "setterArgs": [4, false] + }, + { + "desiredValue": false, + "getterArgs": [5], + "setter": "setReq", + "setterArgs": [5, false] + } + ] + }, "LinearSum": { "w1": "1", "w2": "1", @@ -415,7 +619,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -423,9 +627,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "86400", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "86400", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { @@ -554,6 +758,74 @@ } ] }, + "CommitManagerV2": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + } + ] + }, + "CommitManagerV2U1": { + "reqs": [ + { + "desiredValue": true, + "getterArgs": [0], + "setter": "setReq", + "setterArgs": [0, true] + }, + { + "desiredValue": false, + "getterArgs": [1], + "setter": "setReq", + "setterArgs": [1, false] + }, + { + "desiredValue": false, + "getterArgs": [2], + "setter": "setReq", + "setterArgs": [2, false] + }, + { + "desiredValue": false, + "getterArgs": [3], + "setter": "setReq", + "setterArgs": [3, false] + }, + { + "desiredValue": false, + "getterArgs": [4], + "setter": "setReq", + "setterArgs": [4, false] + }, + { + "desiredValue": false, + "getterArgs": [5], + "setter": "setReq", + "setterArgs": [5, false] + } + ] + }, "LinearSum": { "w1": "1", "w2": "1", @@ -577,7 +849,7 @@ "epochLength": "7776000", "finalizationCommitsNumber": "3", "maxProofWindowOffsetPerc": "75", - "maximumStake": "5000000000000000000000000", + "maximumStake": "2000000000000000000000000", "minProofWindowOffsetPerc": "50", "minimumStake": "50000000000000000000000", "proofWindowDurationPerc": "25", @@ -585,9 +857,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "2419200", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "3600", + "stakeWithdrawalDelay": "2419200", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { diff --git a/index.ts b/index.ts index 8bb7008e..1c0de5e0 100644 --- a/index.ts +++ b/index.ts @@ -122,8 +122,10 @@ export { ServiceAgreementStorageV1U1 as ServiceAgreementStorageV1U1ABI, ServiceAgreementStorageProxy as ServiceAgreementStorageProxyABI, ServiceAgreementV1 as ServiceAgreementV1ABI, - CommitManagerV2 as CommitManagerV1ABI, - CommitManagerV2U1 as CommitManagerV1U1ABI, + CommitManagerV1 as CommitManagerV1ABI, + CommitManagerV1U1 as CommitManagerV1U1ABI, + CommitManagerV2 as CommitManagerV2ABI, + CommitManagerV2U1 as CommitManagerV2U1ABI, ProofManagerV1 as ProofManagerV1ABI, ProofManagerV1U1 as ProofManagerV1U1ABI, ContentAssetStorageV2 as ContentAssetStorageABI, 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/ContentAssetStorageV2.test.ts b/test/v2/unit/ContentAssetStorageV2.test.ts index f76df005..92df2db5 100644 --- a/test/v2/unit/ContentAssetStorageV2.test.ts +++ b/test/v2/unit/ContentAssetStorageV2.test.ts @@ -47,6 +47,8 @@ describe('@v2 @unit ContentAssetStorageV2', function () { 'ShardingTableStorageV2', 'ShardingTableV2', 'StakingV2', + 'CommitManagerV1', + 'CommitManagerV1U1', 'CommitManagerV2', 'CommitManagerV2U1', 'ContentAsset', From 8b9cec82f03ecd481ccf85f71da66f98ccc79378 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 29 Jan 2024 17:06:48 +0100 Subject: [PATCH 27/46] Reworked ShardingTableV2 to be sorted Array instead of Linked List, optimized gas costs for ShardingTable operations + commits --- abi/CommitManagerV2.json | 13 + abi/CommitManagerV2U1.json | 13 + abi/LinearSum.json | 49 ++++ abi/ShardingTableStorageV2.json | 235 +++++++++++------- abi/ShardingTableV2.json | 60 +---- contracts/v2/CommitManagerV2.sol | 147 ++++------- contracts/v2/CommitManagerV2U1.sol | 160 ++++-------- contracts/v2/ProximityScoringProxy.sol | 4 +- contracts/v2/ShardingTable.sol | 155 +++++------- contracts/v2/errors/ShardingTableErrors.sol | 17 +- contracts/v2/scoring/LinearSum.sol | 16 +- contracts/v2/storage/ShardingTableStorage.sol | 84 ++++--- .../v2/structs/ShardingTableStructsV2.sol | 2 - test/v1/unit/ShardingTableStorage.test.ts | 1 - test/v2/unit/ContentAssetStorageV2.test.ts | 1 + test/v2/unit/ShardingTableV2.test.ts | 67 ++--- 16 files changed, 471 insertions(+), 553 deletions(-) diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json index c5583e37..b6374950 100644 --- a/abi/CommitManagerV2.json +++ b/abi/CommitManagerV2.json @@ -547,6 +547,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "linearSum", + "outputs": [ + { + "internalType": "contract LinearSum", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "name", diff --git a/abi/CommitManagerV2U1.json b/abi/CommitManagerV2U1.json index b47b2591..ec61fb8c 100644 --- a/abi/CommitManagerV2U1.json +++ b/abi/CommitManagerV2U1.json @@ -680,6 +680,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "linearSum", + "outputs": [ + { + "internalType": "contract LinearSum", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "name", diff --git a/abi/LinearSum.json b/abi/LinearSum.json index 2bb7f7dd..bcbb7b7b 100644 --- a/abi/LinearSum.json +++ b/abi/LinearSum.json @@ -58,6 +58,55 @@ "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": [ { diff --git a/abi/ShardingTableStorageV2.json b/abi/ShardingTableStorageV2.json index 235632d0..613bb69f 100644 --- a/abi/ShardingTableStorageV2.json +++ b/abi/ShardingTableStorageV2.json @@ -22,16 +22,6 @@ "name": "identityId", "type": "uint72" }, - { - "internalType": "uint72", - "name": "prevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "nextIdentityId", - "type": "uint72" - }, { "internalType": "uint72", "name": "index", @@ -84,12 +74,55 @@ "type": "uint72" } ], - "name": "getIdentityIdByIndex", + "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", @@ -126,16 +159,6 @@ "internalType": "uint72", "name": "identityId", "type": "uint72" - }, - { - "internalType": "uint72", - "name": "prevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "nextIdentityId", - "type": "uint72" } ], "internalType": "struct ShardingTableStructsV2.Node[]", @@ -150,11 +173,21 @@ "inputs": [ { "internalType": "uint72", - "name": "identityId", + "name": "leftEdgeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "closestNodeIndex", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "rightEdgeIndex", "type": "uint72" } ], - "name": "getNode", + "name": "getNeighborhoodBoundaryByIndexes", "outputs": [ { "components": [ @@ -172,15 +205,49 @@ "internalType": "uint72", "name": "identityId", "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "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": "uint72", - "name": "prevIdentityId", + "name": "index", "type": "uint72" }, { "internalType": "uint72", - "name": "nextIdentityId", + "name": "identityId", "type": "uint72" } ], @@ -196,11 +263,11 @@ "inputs": [ { "internalType": "uint72", - "name": "index", + "name": "identityId", "type": "uint72" } ], - "name": "getNodeByIndex", + "name": "getNode", "outputs": [ { "components": [ @@ -218,16 +285,6 @@ "internalType": "uint72", "name": "identityId", "type": "uint72" - }, - { - "internalType": "uint72", - "name": "prevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "nextIdentityId", - "type": "uint72" } ], "internalType": "struct ShardingTableStructsV2.Node", @@ -239,15 +296,38 @@ "type": "function" }, { - "inputs": [], - "name": "head", - "outputs": [ + "inputs": [ { "internalType": "uint72", - "name": "", + "name": "index", "type": "uint72" } ], + "name": "getNodeByIndex", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "hashRingPosition", + "type": "uint256" + }, + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "internalType": "struct ShardingTableStructsV2.Node", + "name": "", + "type": "tuple" + } + ], "stateMutability": "view", "type": "function" }, @@ -288,18 +368,19 @@ "inputs": [ { "internalType": "uint72", - "name": "leftNodeIdentityId", + "name": "", "type": "uint72" - }, + } + ], + "name": "indexToIdentityId", + "outputs": [ { "internalType": "uint72", - "name": "rightNodeIdentityId", + "name": "", "type": "uint72" } ], - "name": "link", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -334,82 +415,52 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "nodesCount", - "outputs": [ - { - "internalType": "uint72", - "name": "", - "type": "uint72" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { "internalType": "uint72", - "name": "identityId", + "name": "index", "type": "uint72" } ], - "name": "setHead", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint72", - "name": "index", - "type": "uint72" - }, + "name": "nodeExistsByIndex", + "outputs": [ { - "internalType": "uint72", - "name": "identityId", - "type": "uint72" + "internalType": "bool", + "name": "", + "type": "bool" } ], - "name": "setIdentityId", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "inputs": [ - { - "internalType": "uint72", - "name": "identityId", - "type": "uint72" - }, + "inputs": [], + "name": "nodesCount", + "outputs": [ { "internalType": "uint72", - "name": "newNextIdentityId", + "name": "", "type": "uint72" } ], - "name": "setNextIdentityId", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint72", - "name": "identityId", + "name": "index", "type": "uint72" }, { "internalType": "uint72", - "name": "newPrevIdentityId", + "name": "identityId", "type": "uint72" } ], - "name": "setPrevIdentityId", + "name": "setIdentityId", "outputs": [], "stateMutability": "nonpayable", "type": "function" diff --git a/abi/ShardingTableV2.json b/abi/ShardingTableV2.json index 8b1a1400..4d2dc2a3 100644 --- a/abi/ShardingTableV2.json +++ b/abi/ShardingTableV2.json @@ -22,18 +22,13 @@ "name": "hashRingPosition", "type": "uint256" }, - { - "internalType": "uint72", - "name": "nextIdentityId", - "type": "uint72" - }, { "internalType": "uint256", "name": "nextHashRingPosition", "type": "uint256" } ], - "name": "InvalidNextIdentityId", + "name": "InvalidIndexWithRespectToNextNode", "type": "error" }, { @@ -48,49 +43,13 @@ "name": "hashRingPosition", "type": "uint256" }, - { - "internalType": "uint72", - "name": "prevIdentityId", - "type": "uint72" - }, { "internalType": "uint256", "name": "prevHashRingPosition", "type": "uint256" } ], - "name": "InvalidPreviousIdentityId", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint72", - "name": "identityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "sentPrevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "realPrevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "sentNextIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "realNextIdentityId", - "type": "uint72" - } - ], - "name": "InvalidPreviousOrNextIdentityId", + "name": "InvalidIndexWithRespectToPreviousNode", "type": "error" }, { @@ -256,6 +215,11 @@ }, { "inputs": [ + { + "internalType": "uint72", + "name": "index", + "type": "uint72" + }, { "internalType": "uint72", "name": "identityId", @@ -273,16 +237,6 @@ "internalType": "uint72", "name": "identityId", "type": "uint72" - }, - { - "internalType": "uint72", - "name": "prevIdentityId", - "type": "uint72" - }, - { - "internalType": "uint72", - "name": "nextIdentityId", - "type": "uint72" } ], "name": "insertNode", diff --git a/contracts/v2/CommitManagerV2.sol b/contracts/v2/CommitManagerV2.sol index 851c984d..ff94e277 100644 --- a/contracts/v2/CommitManagerV2.sol +++ b/contracts/v2/CommitManagerV2.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.16; import {HashingProxy} from "../v1/HashingProxy.sol"; +import {LinearSum} from "./scoring/LinearSum.sol"; import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; import {StakingV2} from "./Staking.sol"; import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; @@ -23,6 +24,7 @@ import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; +import {NULL} from "../v1/constants/ShardingTableConstants.sol"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( @@ -38,11 +40,12 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { 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]; HashingProxy public hashingProxy; + LinearSum public linearSum; ProximityScoringProxy public proximityScoringProxy; StakingV2 public stakingContract; IdentityStorageV2 public identityStorage; @@ -60,6 +63,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); + linearSum = LinearSum(proximityScoringProxy.getScoreFunctionContractAddress(_LINEAR_SUM_ID)); stakingContract = StakingV2(hub.getContractAddress("Staking")); identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); @@ -160,13 +164,11 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { if (!sasProxy.agreementV1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); - uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); - - if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( agreementId, args.epoch, - proximityScoreFunctionPairId, + sasProxy.getAgreementScoreFunctionId(agreementId), block.timestamp ); @@ -200,7 +202,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( agreementId, args.epoch, - proximityScoreFunctionPairId, args.hashFunctionId, args.keyword, args.closestNodeIndex, @@ -208,15 +209,13 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, + uint256 distance = linearSum.calculateDistance( args.hashFunctionId, - args.keyword, - profileStorage.getNodeId(identityId) + profileStorage.getNodeId(identityId), + args.keyword ); - uint40 score = proximityScoringProxy.callScoreFunction( - proximityScoreFunctionPairId, + uint40 score = linearSum.calculateScore( distance, maxDistance, nodesCount, @@ -243,7 +242,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function _verifyNeighborhood( bytes32 agreementId, uint16 epoch, - uint8 proximityScoreFunctionPairId, uint8 hashFunctionId, bytes calldata keyword, uint72 closestNodeIndex, @@ -253,17 +251,44 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStorageV2 sts = shardingTableStorage; ProfileStorage ps = profileStorage; - ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(closestNodeIndex); - ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(leftEdgeNodeIndex); - ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(rightEdgeNodeIndex); + ( + ShardingTableStructsV2.Node memory leftEdgeNode, + ShardingTableStructsV2.Node memory closestNode, + ShardingTableStructsV2.Node memory rightEdgeNode + ) = sts.getNeighborhoodBoundaryByIndexes(leftEdgeNodeIndex, closestNodeIndex, rightEdgeNodeIndex); - // Verify that closestNode is in smaller arc between leftNode and rightNode 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 nodesInBetweenClockwise = ( + (rightEdgeNode.index > leftEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index - 1 + : leftEdgeNode.index - rightEdgeNode.index - 1 + ); + uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) + ? nodesInBetweenClockwise + 2 + : nodesCount - nodesInBetweenClockwise; + + (uint72 closestPrevIdentityId, uint72 closestNextIdentityId) = sts.getAdjacentIdentityIdsByIndex( + closestNodeIndex + ); + uint72 rightEdgeNextIdentityId = sts.indexToIdentityId(rightEdgeNodeIndex + 1); + uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) : NULL; + + (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = linearSum + .calculateNeighborhoodBoundaryDistances( + hashFunctionId, + ps.getNodeId(leftEdgeNode.identityId), + ps.getNodeId(closestNode.identityId), + ps.getNodeId(rightEdgeNode.identityId), + keyword + ); + + // Verify that closestNode is in smaller arc between leftNode and rightNode if (!isBetween) revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( agreementId, @@ -275,16 +300,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); // Verify number of nodes between leftNode and rightNode (should be R2) - uint72 nodesCount = sts.nodesCount(); - uint72 nodesInBetweenClockwise = ( - (rightEdgeNode.index > leftEdgeNode.index) - ? rightEdgeNode.index - leftEdgeNode.index - 1 - : leftEdgeNode.index - rightEdgeNode.index - 1 - ); - uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) - ? nodesInBetweenClockwise + 2 - : nodesCount - nodesInBetweenClockwise; - if (neighborhoodSize != parametersStorage.r2()) revert CommitManagerErrorsV2.InvalidNeighborhoodSize( agreementId, @@ -298,108 +313,46 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ); // Verify that closestNode is indeed closest - uint256 closestDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.identityId) - ); - if ( closestDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.prevIdentityId) - ) || - closestDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.nextIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || + closestDistance > linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidClosestNode( agreementId, epoch, closestNodeIndex, closestDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.prevIdentityId) - ), - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.nextIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), block.timestamp ); // Verify that leftNode is indeed the left edge of the Neighborhood - uint256 leftEdgeDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.identityId) - ); - if ( leftEdgeDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.nextIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, epoch, leftEdgeNodeIndex, leftEdgeDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.nextIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp ); // Verify that rightNode is indeed the right edge of the Neighborhood - uint256 rightEdgeDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.identityId) - ); - if ( rightEdgeDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.prevIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)) ) revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, epoch, rightEdgeNodeIndex, rightEdgeDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.prevIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp ); diff --git a/contracts/v2/CommitManagerV2U1.sol b/contracts/v2/CommitManagerV2U1.sol index e069a1d5..d10f1f79 100644 --- a/contracts/v2/CommitManagerV2U1.sol +++ b/contracts/v2/CommitManagerV2U1.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.16; import {HashingProxy} from "../v1/HashingProxy.sol"; +import {LinearSum} from "./scoring/LinearSum.sol"; import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; import {StakingV2} from "./Staking.sol"; import {ContentAssetStorageV2} from "./storage/assets/ContentAssetStorage.sol"; @@ -27,6 +28,7 @@ import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV1U1} from "../v1/errors/ServiceAgreementErrorsV1U1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; +import {NULL} from "../v1/constants/ShardingTableConstants.sol"; contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( @@ -52,11 +54,12 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { 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]; HashingProxy public hashingProxy; + LinearSum public linearSum; ProximityScoringProxy public proximityScoringProxy; StakingV2 public stakingContract; ContentAssetStorageV2 public contentAssetStorage; @@ -74,6 +77,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); + linearSum = LinearSum(proximityScoringProxy.getScoreFunctionContractAddress(_LINEAR_SUM_ID)); stakingContract = StakingV2(hub.getContractAddress("Staking")); contentAssetStorage = ContentAssetStorageV2(hub.getAssetStorageAddress("ContentAssetStorage")); identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); @@ -203,13 +207,11 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); - uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); - - if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( agreementId, args.epoch, - proximityScoreFunctionPairId, + sasProxy.getAgreementScoreFunctionId(agreementId), block.timestamp ); @@ -246,7 +248,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( agreementId, args.epoch, - proximityScoreFunctionPairId, args.hashFunctionId, args.keyword, args.closestNodeIndex, @@ -254,15 +255,13 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, + uint256 distance = linearSum.calculateDistance( args.hashFunctionId, args.keyword, profileStorage.getNodeId(identityId) ); - uint40 score = proximityScoringProxy.callScoreFunction( - proximityScoreFunctionPairId, + uint40 score = linearSum.calculateScore( distance, maxDistance, nodesCount, @@ -303,13 +302,11 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (!sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); - uint8 proximityScoreFunctionPairId = sasProxy.getAgreementScoreFunctionId(agreementId); - - if (proximityScoreFunctionPairId == _LOG2PLDSF_ID) + if (sasProxy.getAgreementScoreFunctionId(agreementId) != _LINEAR_SUM_ID) revert ServiceAgreementErrorsV2.InvalidProximityScoreFunctionsPairId( agreementId, args.epoch, - proximityScoreFunctionPairId, + sasProxy.getAgreementScoreFunctionId(agreementId), block.timestamp ); @@ -344,7 +341,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { (uint72 nodesCount, uint256 maxDistance) = _verifyNeighborhood( agreementId, args.epoch, - proximityScoreFunctionPairId, args.hashFunctionId, args.keyword, args.closestNodeIndex, @@ -352,15 +348,13 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, + uint256 distance = linearSum.calculateDistance( args.hashFunctionId, - args.keyword, - profileStorage.getNodeId(identityId) + profileStorage.getNodeId(identityId), + args.keyword ); - uint40 score = proximityScoringProxy.callScoreFunction( - proximityScoreFunctionPairId, + uint40 score = linearSum.calculateScore( distance, maxDistance, nodesCount, @@ -418,7 +412,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { function _verifyNeighborhood( bytes32 agreementId, uint16 epoch, - uint8 proximityScoreFunctionPairId, uint8 hashFunctionId, bytes calldata keyword, uint72 closestNodeIndex, @@ -428,17 +421,44 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ShardingTableStorageV2 sts = shardingTableStorage; ProfileStorage ps = profileStorage; - ShardingTableStructsV2.Node memory closestNode = sts.getNodeByIndex(closestNodeIndex); - ShardingTableStructsV2.Node memory leftEdgeNode = sts.getNodeByIndex(leftEdgeNodeIndex); - ShardingTableStructsV2.Node memory rightEdgeNode = sts.getNodeByIndex(rightEdgeNodeIndex); + ( + ShardingTableStructsV2.Node memory leftEdgeNode, + ShardingTableStructsV2.Node memory closestNode, + ShardingTableStructsV2.Node memory rightEdgeNode + ) = sts.getNeighborhoodBoundaryByIndexes(leftEdgeNodeIndex, closestNodeIndex, rightEdgeNodeIndex); - // Verify that closestNode is in smaller arc between leftNode and rightNode 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 nodesInBetweenClockwise = ( + (rightEdgeNode.index > leftEdgeNode.index) + ? rightEdgeNode.index - leftEdgeNode.index - 1 + : leftEdgeNode.index - rightEdgeNode.index - 1 + ); + uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) + ? nodesInBetweenClockwise + 2 + : nodesCount - nodesInBetweenClockwise; + + (uint72 closestPrevIdentityId, uint72 closestNextIdentityId) = sts.getAdjacentIdentityIdsByIndex( + closestNodeIndex + ); + uint72 rightEdgeNextIdentityId = sts.indexToIdentityId(rightEdgeNodeIndex + 1); + uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) : NULL; + + (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = linearSum + .calculateNeighborhoodBoundaryDistances( + hashFunctionId, + ps.getNodeId(leftEdgeNode.identityId), + ps.getNodeId(closestNode.identityId), + ps.getNodeId(rightEdgeNode.identityId), + keyword + ); + + // Verify that closestNode is in smaller arc between leftNode and rightNode if (!isBetween) revert CommitManagerErrorsV2.ClosestNodeNotInNeighborhood( agreementId, @@ -450,16 +470,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ); // Verify number of nodes between leftNode and rightNode (should be R2) - uint72 nodesCount = sts.nodesCount(); - uint72 nodesInBetweenClockwise = ( - (rightEdgeNode.index > leftEdgeNode.index) - ? rightEdgeNode.index - leftEdgeNode.index - 1 - : leftEdgeNode.index - rightEdgeNode.index - 1 - ); - uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) - ? nodesInBetweenClockwise + 2 - : nodesCount - nodesInBetweenClockwise; - if (neighborhoodSize != parametersStorage.r2()) revert CommitManagerErrorsV2.InvalidNeighborhoodSize( agreementId, @@ -473,108 +483,46 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ); // Verify that closestNode is indeed closest - uint256 closestDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.identityId) - ); - if ( closestDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.prevIdentityId) - ) || - closestDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.nextIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || + closestDistance > linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidClosestNode( agreementId, epoch, closestNodeIndex, closestDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.prevIdentityId) - ), - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(closestNode.nextIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), block.timestamp ); // Verify that leftNode is indeed the left edge of the Neighborhood - uint256 leftEdgeDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.identityId) - ); - if ( leftEdgeDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.nextIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, epoch, leftEdgeNodeIndex, leftEdgeDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.nextIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp ); // Verify that rightNode is indeed the right edge of the Neighborhood - uint256 rightEdgeDistance = proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(rightEdgeNode.identityId) - ); - if ( rightEdgeDistance > - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.prevIdentityId) - ) + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)) ) revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, epoch, rightEdgeNodeIndex, rightEdgeDistance, - proximityScoringProxy.callProximityFunction( - proximityScoreFunctionPairId, - hashFunctionId, - keyword, - ps.getNodeId(leftEdgeNode.prevIdentityId) - ), + linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp ); diff --git a/contracts/v2/ProximityScoringProxy.sol b/contracts/v2/ProximityScoringProxy.sol index 883c3a19..f0713009 100644 --- a/contracts/v2/ProximityScoringProxy.sol +++ b/contracts/v2/ProximityScoringProxy.sol @@ -74,8 +74,8 @@ contract ProximityScoringProxy is Named, Versioned, ContractStatus { function callProximityFunction( uint8 proximityFunctionId, uint8 hashFunctionId, - bytes memory nodeId, - bytes memory keyword + bytes calldata nodeId, + bytes calldata keyword ) external view returns (uint256) { IProximityScoreFunctionsPair proximityScoreFunctionsPair = IProximityScoreFunctionsPair( scoreFunctionSet.get(proximityFunctionId).addr diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index bbef6660..1d3f6a41 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -51,18 +51,17 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { function getShardingTable() external view returns (ShardingTableStructsV2.NodeInfo[] memory) { ShardingTableStorageV2 sts = shardingTableStorage; - return _getShardingTable(sts.head(), sts.nodesCount()); + return _getShardingTable(sts.indexToIdentityId(0), sts.nodesCount()); } function insertNode(uint72 identityId) external onlyContracts { uint256 newNodeHashRingPosition = uint256(profileStorage.getNodeAddress(identityId, 1)); - (uint72 prevIdentityId, uint72 nextIdentityId) = _binarySearchForPosition(newNodeHashRingPosition); - _insertNode(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId); + _insertNode(_binarySearchForIndex(newNodeHashRingPosition), identityId, newNodeHashRingPosition); } - function insertNode(uint72 identityId, uint72 prevIdentityId, uint72 nextIdentityId) external onlyContracts { - _insertNode(uint256(profileStorage.getNodeAddress(identityId, 1)), identityId, prevIdentityId, nextIdentityId); + function insertNode(uint72 index, uint72 identityId) external onlyContracts { + _insertNode(index, identityId, uint256(profileStorage.getNodeAddress(identityId, 1))); } function removeNode(uint72 identityId) external onlyContracts { @@ -70,15 +69,9 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ShardingTableStructsV2.Node memory nodeToRemove = sts.getNode(identityId); - // If removing Head => set new Head (can also be 0 if there is only 1 node in the list) - if (nodeToRemove.prevIdentityId == NULL) sts.setHead(nodeToRemove.nextIdentityId); - - // Link left and right nodes (both can be NULL, there is a check in link function) - sts.link(nodeToRemove.prevIdentityId, nodeToRemove.nextIdentityId); - // Decrement indexes of all nodes after removed one + add pointers to identityId for changed indexes uint72 index = nodeToRemove.index; - uint72 nextIdentityId = nodeToRemove.nextIdentityId; + uint72 nextIdentityId = sts.indexToIdentityId(nodeToRemove.index + 1); while (nextIdentityId != NULL) { sts.decrementNodeIndex(nextIdentityId); sts.setIdentityId(index, nextIdentityId); @@ -86,135 +79,100 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { unchecked { index += 1; } - nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + + 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, 0); + sts.setIdentityId(index, NULL); sts.decrementNodesCount(); emit NodeRemoved(identityId, profileStorage.getNodeId(identityId)); } - function _binarySearchForPosition(uint256 hashRingPosition) internal virtual returns (uint72, uint72) { + function _binarySearchForIndex(uint256 hashRingPosition) internal virtual returns (uint72) { ShardingTableStorageV2 sts = shardingTableStorage; - int72 left; - int72 mid; - int72 right = int72(sts.nodesCount()) - 1; + uint72 nodesCount = sts.nodesCount(); + + if (nodesCount == 0) { + return 0; + } + + uint72 left; + uint72 mid; + uint72 right = nodesCount - 1; - uint72 prevIdentityId; - uint72 nextIdentityId; while (left <= right) { mid = (left + right) / 2; - ShardingTableStructsV2.Node memory currentNode = sts.getNodeByIndex(uint72(mid)); + uint256 currentHashRingPosition = sts.getHashRingPositionByIndex(mid); - if (currentNode.hashRingPosition < hashRingPosition) { + if (hashRingPosition > currentHashRingPosition) { // Node is in the right half of the range, move left pointers - prevIdentityId = currentNode.identityId; left = mid + 1; - } else if (currentNode.hashRingPosition > hashRingPosition) { + } 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 - nextIdentityId = currentNode.identityId; right = mid - 1; } else { // Exact match found - prevIdentityId = currentNode.identityId; - nextIdentityId = currentNode.nextIdentityId; - break; + return mid; } } - return (prevIdentityId, nextIdentityId); + return left; } - function _insertNode( - uint256 newNodeHashRingPosition, - uint72 identityId, - uint72 prevIdentityId, - uint72 nextIdentityId - ) internal virtual { + function _insertNode(uint72 index, uint72 identityId, uint256 newNodeHashRingPosition) internal virtual { ShardingTableStorageV2 sts = shardingTableStorage; ProfileStorage ps = profileStorage; - ShardingTableStructsV2.Node memory prevNode = sts.getNode(prevIdentityId); - - // 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 < prevNode.hashRingPosition) - revert ShardingTableErrors.InvalidPreviousIdentityId( - identityId, - newNodeHashRingPosition, - prevIdentityId, - prevNode.hashRingPosition - ); + 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.getNode(nextIdentityId); + 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.InvalidNextIdentityId( + revert ShardingTableErrors.InvalidIndexWithRespectToNextNode( identityId, newNodeHashRingPosition, - nextIdentityId, nextNode.hashRingPosition ); - // Verify that prevNode and nextNode are direct neighbors before inserting a new node between them - if ( - (prevIdentityId != NULL && nextIdentityId != prevNode.nextIdentityId) || - (nextIdentityId != NULL && prevIdentityId != nextNode.prevIdentityId) - ) - revert ShardingTableErrors.InvalidPreviousOrNextIdentityId( - identityId, - prevIdentityId, - nextNode.prevIdentityId, - nextIdentityId, - prevNode.nextIdentityId - ); - - uint72 index; - if (nextIdentityId == NULL) { - // Inserting a new Tail - if (prevIdentityId != NULL) { - // The list is not empty, calculate the new Tail's index - index = prevNode.index + 1; - } else { - // The list is empty, start with index 0 - index = 0; - } - } else { - // Inserting a node before the nextNode - index = nextNode.index; - } - // Create node object + set index pointer to new identityId + increment total nodes count - sts.createNodeObject(newNodeHashRingPosition, identityId, prevIdentityId, nextIdentityId, index); + sts.createNodeObject(newNodeHashRingPosition, identityId, index); sts.setIdentityId(index, identityId); sts.incrementNodesCount(); - // If Head => add Head pointer - // If not Head => add the link between prevNode and new Node - if (prevIdentityId == NULL) sts.setHead(identityId); - else sts.link(prevIdentityId, identityId); - - // If not Tail => add the link between new Node and nextNode - if (nextIdentityId != NULL) sts.link(identityId, nextIdentityId); - // Increment indexes of all nodes after inserted one + add pointers to identityId for changed indexes - unchecked { - index += 1; - } + uint72 currentIdentityId; + uint72 nextIdentityId = nextNode.identityId; while (nextIdentityId != NULL) { - sts.incrementNodeIndex(nextIdentityId); - sts.setIdentityId(index, nextIdentityId); - unchecked { index += 1; } - nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; + + currentIdentityId = nextIdentityId; + nextIdentityId = sts.indexToIdentityId(index); + + sts.incrementNodeIndex(currentIdentityId); + sts.setIdentityId(index, currentIdentityId); } emit NodeAdded( @@ -244,9 +202,9 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { StakingStorage ss = stakingStorage; uint72 nextIdentityId = startingIdentityId; - uint72 i; - while ((i < nodesNumber) && (nextIdentityId != NULL)) { - nodesPage[i] = ShardingTableStructsV2.NodeInfo({ + uint72 index; + while ((index < nodesNumber) && (nextIdentityId != NULL)) { + nodesPage[index] = ShardingTableStructsV2.NodeInfo({ hashRingPosition: uint256(ps.getNodeAddress(nextIdentityId, 1)), nodeId: ps.getNodeId(nextIdentityId), identityId: nextIdentityId, @@ -254,11 +212,10 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { stake: ss.totalStakes(nextIdentityId) }); - nextIdentityId = sts.getNode(nextIdentityId).nextIdentityId; - unchecked { - i += 1; + index += 1; } + nextIdentityId = sts.indexToIdentityId(index); } return nodesPage; diff --git a/contracts/v2/errors/ShardingTableErrors.sol b/contracts/v2/errors/ShardingTableErrors.sol index ff55b50b..f17212f5 100644 --- a/contracts/v2/errors/ShardingTableErrors.sol +++ b/contracts/v2/errors/ShardingTableErrors.sol @@ -3,23 +3,10 @@ pragma solidity ^0.8.16; library ShardingTableErrors { - error InvalidPreviousIdentityId( + error InvalidIndexWithRespectToPreviousNode( uint72 identityId, uint256 hashRingPosition, - uint72 prevIdentityId, uint256 prevHashRingPosition ); - error InvalidNextIdentityId( - uint72 identityId, - uint256 hashRingPosition, - uint72 nextIdentityId, - uint256 nextHashRingPosition - ); - error InvalidPreviousOrNextIdentityId( - uint72 identityId, - uint72 sentPrevIdentityId, - uint72 realPrevIdentityId, - uint72 sentNextIdentityId, - uint72 realNextIdentityId - ); + error InvalidIndexWithRespectToNextNode(uint72 identityId, uint256 hashRingPosition, uint256 nextHashRingPosition); } diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 114408f7..374c1fc6 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -60,7 +60,7 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint8 hashFunctionId, bytes calldata nodeId, bytes calldata keyword - ) external view returns (uint256) { + ) public view returns (uint256) { uint256 nodePositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, nodeId)); uint256 keywordPositionOnHashRing = uint256(hashingProxy.callHashFunction(hashFunctionId, keyword)); @@ -77,6 +77,20 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende ); } + 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; diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 78b2b414..7030d8a4 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -12,17 +12,15 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { string private constant _NAME = "ShardingTableStorage"; string private constant _VERSION = "2.0.0"; - uint72 public head; uint72 public nodesCount; // identityId => Node mapping(uint72 => ShardingTableStructsV2.Node) internal nodes; // index => identityId - mapping(uint72 => uint72) internal identityIds; + mapping(uint72 => uint72) public indexToIdentityId; - constructor(address hubAddress) HubDependent(hubAddress) { - head = NULL; - } + // solhint-disable-next-line no-empty-blocks + constructor(address hubAddress) HubDependent(hubAddress) {} function name() external pure virtual override returns (string memory) { return _NAME; @@ -40,23 +38,11 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { nodesCount--; } - function setHead(uint72 identityId) external onlyContracts { - head = identityId; - } - - function createNodeObject( - uint256 hashRingPosition, - uint72 identityId, - uint72 prevIdentityId, - uint72 nextIdentityId, - uint72 index - ) external onlyContracts { + function createNodeObject(uint256 hashRingPosition, uint72 identityId, uint72 index) external onlyContracts { nodes[identityId] = ShardingTableStructsV2.Node({ hashRingPosition: hashRingPosition, index: index, - identityId: identityId, - prevIdentityId: prevIdentityId, - nextIdentityId: nextIdentityId + identityId: identityId }); } @@ -72,12 +58,8 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { return nodes[identityId].identityId != 0; } - function setPrevIdentityId(uint72 identityId, uint72 newPrevIdentityId) external onlyContracts { - nodes[identityId].prevIdentityId = newPrevIdentityId; - } - - function setNextIdentityId(uint72 identityId, uint72 newNextIdentityId) external onlyContracts { - nodes[identityId].nextIdentityId = newNextIdentityId; + function getHashRingPosition(uint72 identityId) external view returns (uint256) { + return nodes[identityId].hashRingPosition; } function incrementNodeIndex(uint72 identityId) external onlyContracts { @@ -88,16 +70,48 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { nodes[identityId].index -= 1; } - function getIdentityIdByIndex(uint72 index) external view returns (uint72) { - return identityIds[index]; - } - function setIdentityId(uint72 index, uint72 identityId) external onlyContracts { - identityIds[index] = identityId; + indexToIdentityId[index] = identityId; } function getNodeByIndex(uint72 index) external view returns (ShardingTableStructsV2.Node memory) { - return nodes[identityIds[index]]; + 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( @@ -109,7 +123,7 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { ShardingTableStructsV2.Node memory currentNode = nodes[firstIdentityId]; for (uint256 i; i < nodesNumber; ) { nodesPage[i] = currentNode; - currentNode = nodes[currentNode.nextIdentityId]; + currentNode = nodes[indexToIdentityId[currentNode.index + 1]]; unchecked { i++; } @@ -117,10 +131,4 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { return nodesPage; } - - function link(uint72 leftNodeIdentityId, uint72 rightNodeIdentityId) external onlyContracts { - if (leftNodeIdentityId != NULL) nodes[leftNodeIdentityId].nextIdentityId = rightNodeIdentityId; - - if (rightNodeIdentityId != NULL) nodes[rightNodeIdentityId].prevIdentityId = leftNodeIdentityId; - } } diff --git a/contracts/v2/structs/ShardingTableStructsV2.sol b/contracts/v2/structs/ShardingTableStructsV2.sol index 7b8268b8..24c18d6f 100644 --- a/contracts/v2/structs/ShardingTableStructsV2.sol +++ b/contracts/v2/structs/ShardingTableStructsV2.sol @@ -15,7 +15,5 @@ library ShardingTableStructsV2 { uint256 hashRingPosition; uint72 index; uint72 identityId; - uint72 prevIdentityId; - uint72 nextIdentityId; } } diff --git a/test/v1/unit/ShardingTableStorage.test.ts b/test/v1/unit/ShardingTableStorage.test.ts index 1ab45165..b1e582d5 100644 --- a/test/v1/unit/ShardingTableStorage.test.ts +++ b/test/v1/unit/ShardingTableStorage.test.ts @@ -56,7 +56,6 @@ describe('@v1 @unit ShardingTableStorage Contract', function () { const receipt = await singleIdentityId.wait(); identityId = receipt.events?.[3].args?.identityId.toNumber(); - console.log(identityId); idsArray.push(identityId); } return idsArray; diff --git a/test/v2/unit/ContentAssetStorageV2.test.ts b/test/v2/unit/ContentAssetStorageV2.test.ts index 92df2db5..ff5249cb 100644 --- a/test/v2/unit/ContentAssetStorageV2.test.ts +++ b/test/v2/unit/ContentAssetStorageV2.test.ts @@ -42,6 +42,7 @@ describe('@v2 @unit ContentAssetStorageV2', function () { await hre.deployments.fixture([ 'HubV2', 'Token', + 'LinearSum', 'ContentAssetStorageV2', 'IdentityStorageV2', 'ShardingTableStorageV2', diff --git a/test/v2/unit/ShardingTableV2.test.ts b/test/v2/unit/ShardingTableV2.test.ts index 82f03688..7b9aa053 100644 --- a/test/v2/unit/ShardingTableV2.test.ts +++ b/test/v2/unit/ShardingTableV2.test.ts @@ -27,12 +27,6 @@ describe('@v2 @unit ShardingTableV2 contract', function () { // 3 1 2 4 5 - // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId1)).toString()); - // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId2)).toString()); - // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId3)).toString()); - // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId4)).toString()); - // console.log(hre.ethers.BigNumber.from(hre.ethers.utils.sha256(nodeId5)).toString()); - async function deployShardingTableFixture(): Promise { await hre.deployments.fixture(['ShardingTableV2', 'IdentityStorageV2', 'StakingV2', 'Profile'], { keepExistingDeployments: false, @@ -83,17 +77,6 @@ describe('@v2 @unit ShardingTableV2 contract', function () { expect(node.identityId.toNumber(), 'Invalid node on this position').to.equal(identityIds[i]); expect(node.index.toNumber(), 'Invalid node index').to.equal(i); - if (i === 0) { - expect(node.prevIdentityId.toNumber(), 'Invalid prevIdentityId').to.equal(0); - } else { - expect(node.prevIdentityId.toNumber(), 'Invalid prevIdentityId').to.equal(identityIds[i - 1]); - } - - if (i === nodesCount - 1) { - expect(node.nextIdentityId.toNumber(), 'Invalid nextIdentityId').to.equal(0); - } else { - expect(node.nextIdentityId.toNumber(), 'Invalid nextIdentityId').to.equal(identityIds[i + 1]); - } } } @@ -114,23 +97,23 @@ describe('@v2 @unit ShardingTableV2 contract', function () { const profiles = await createMultipleProfiles(); // 2 - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], 0, 0); + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); await validateShardingTableResult([2]); // 3 2 - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[2], 0, profiles[1]); + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[2]); await validateShardingTableResult([3, 2]); // 3 2 5 - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[4], profiles[1], 0); + await ShardingTable['insertNode(uint72,uint72)'](2, profiles[4]); await validateShardingTableResult([3, 2, 5]); // 3 2 4 5 - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], profiles[4]); + await ShardingTable['insertNode(uint72,uint72)'](2, profiles[3]); await validateShardingTableResult([3, 2, 4, 5]); // 3 1 2 4 5 - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], profiles[1]); + await ShardingTable['insertNode(uint72,uint72)'](1, profiles[0]); await validateShardingTableResult([3, 1, 2, 4, 5]); }); @@ -161,43 +144,33 @@ describe('@v2 @unit ShardingTableV2 contract', function () { it('Insert node with invalid prevIdentityId expect to fail', async () => { const profiles = await createMultipleProfiles(); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], 0, 0); + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); - await expect( - ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[1], 0), - ).to.be.revertedWithCustomError(ShardingTable, 'InvalidPreviousIdentityId'); + 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,uint72)'](profiles[1], 0, 0); - - await expect( - ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], 0, profiles[1]), - ).to.be.revertedWithCustomError(ShardingTable, 'InvalidNextIdentityId'); - }); - - it('Insert node with invalid prevIdentityId and nextIdentityId expect to fail', async () => { - const profiles = await createMultipleProfiles(); - - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[2], 0, 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], profiles[2], 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], 0); + await ShardingTable['insertNode(uint72,uint72)'](0, profiles[1]); - await expect( - ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], profiles[3]), - ).to.be.revertedWithCustomError(ShardingTable, 'InvalidPreviousOrNextIdentityId'); + 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,uint72)'](profiles[2], 0, 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[0], profiles[2], 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[1], profiles[0], 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[3], profiles[1], 0); - await ShardingTable['insertNode(uint72,uint72,uint72)'](profiles[4], profiles[3], 0); + 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]); From 116601a925b5dfbfcc3c1b410b31e4373c9bb461 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Mon, 29 Jan 2024 22:21:11 +0100 Subject: [PATCH 28/46] Added head() function to the ShardingTableStorageV2, added test for Sharding Table getter --- abi/ShardingTableStorageV2.json | 13 +++++++++++++ contracts/v2/storage/ShardingTableStorage.sol | 4 ++++ test/v2/unit/ShardingTableV2.test.ts | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/abi/ShardingTableStorageV2.json b/abi/ShardingTableStorageV2.json index 613bb69f..c595f88d 100644 --- a/abi/ShardingTableStorageV2.json +++ b/abi/ShardingTableStorageV2.json @@ -331,6 +331,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "head", + "outputs": [ + { + "internalType": "uint72", + "name": "", + "type": "uint72" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "hub", diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 7030d8a4..2a04c2c5 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -58,6 +58,10 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { return nodes[identityId].identityId != 0; } + function head() external view returns (uint72) { + return nodes[0].identityId; + } + function getHashRingPosition(uint72 identityId) external view returns (uint256) { return nodes[identityId].hashRingPosition; } diff --git a/test/v2/unit/ShardingTableV2.test.ts b/test/v2/unit/ShardingTableV2.test.ts index 7b9aa053..e974956c 100644 --- a/test/v2/unit/ShardingTableV2.test.ts +++ b/test/v2/unit/ShardingTableV2.test.ts @@ -78,6 +78,10 @@ describe('@v2 @unit ShardingTableV2 contract', function () { expect(node.index.toNumber(), 'Invalid node index').to.equal(i); } + + const shardingTable = (await ShardingTable['getShardingTable()']()).map((node) => node.identityId.toNumber()); + + expect(shardingTable).to.be.eql(identityIds); } beforeEach(async () => { From 9e27cb20dcc906caf49bdd4d2e933d7babe240f7 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 09:59:04 +0100 Subject: [PATCH 29/46] Updated CommitManagerV2/V2U1 to support old-type commits without introduction of separate contracts, updated deployment scripts and ABI exports --- abi/CommitManagerV2.json | 90 +++++- abi/CommitManagerV2U1.json | 130 ++++++++- ...ommitManagerV2.sol => CommitManagerV1.sol} | 118 ++++++-- ...tManagerV2U1.sol => CommitManagerV1U1.sol} | 236 ++++++++++++--- ..._v2.ts => 031_deploy_commit_manager_v2.ts} | 11 +- ..._v1.ts => 032_deploy_commit_manager_v1.ts} | 6 +- ....ts => 033_deploy_commit_manager_v2_u1.ts} | 11 +- ....ts => 034_deploy_commit_manager_v1_u1.ts} | 6 +- deployments/parameters.json | 272 ------------------ index.ts | 6 +- test/v2/unit/ContentAssetStorageV2.test.ts | 2 - 11 files changed, 521 insertions(+), 367 deletions(-) rename contracts/v2/{CommitManagerV2.sol => CommitManagerV1.sol} (81%) rename contracts/v2/{CommitManagerV2U1.sol => CommitManagerV1U1.sol} (74%) rename deploy/{033_deploy_commit_manager_v2.ts => 031_deploy_commit_manager_v2.ts} (65%) rename deploy/{031_deploy_commit_manager_v1.ts => 032_deploy_commit_manager_v1.ts} (75%) rename deploy/{034_deploy_commit_manager_v2_u1.ts => 033_deploy_commit_manager_v2_u1.ts} (66%) rename deploy/{032_deploy_commit_manager_v1_u1.ts => 034_deploy_commit_manager_v1_u1.ts} (76%) diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json index b6374950..fcdd0a12 100644 --- a/abi/CommitManagerV2.json +++ b/abi/CommitManagerV2.json @@ -262,6 +262,32 @@ "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": [ { @@ -562,36 +588,36 @@ }, { "inputs": [], - "name": "name", + "name": "log2pldsf", "outputs": [ { - "internalType": "string", + "internalType": "contract Log2PLDSF", "name": "", - "type": "string" + "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "parametersStorage", + "name": "name", "outputs": [ { - "internalType": "contract ParametersStorage", + "internalType": "string", "name": "", - "type": "address" + "type": "string" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "profileStorage", + "name": "parametersStorage", "outputs": [ { - "internalType": "contract ProfileStorage", + "internalType": "contract ParametersStorage", "name": "", "type": "address" } @@ -601,10 +627,10 @@ }, { "inputs": [], - "name": "proximityScoringProxy", + "name": "profileStorage", "outputs": [ { - "internalType": "contract ProximityScoringProxy", + "internalType": "contract ProfileStorage", "name": "", "type": "address" } @@ -727,6 +753,46 @@ "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": [ { diff --git a/abi/CommitManagerV2U1.json b/abi/CommitManagerV2U1.json index ec61fb8c..661f4b15 100644 --- a/abi/CommitManagerV2U1.json +++ b/abi/CommitManagerV2U1.json @@ -267,6 +267,32 @@ "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": [ { @@ -695,36 +721,36 @@ }, { "inputs": [], - "name": "name", + "name": "log2pldsf", "outputs": [ { - "internalType": "string", + "internalType": "contract Log2PLDSF", "name": "", - "type": "string" + "type": "address" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "parametersStorage", + "name": "name", "outputs": [ { - "internalType": "contract ParametersStorage", + "internalType": "string", "name": "", - "type": "address" + "type": "string" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { "inputs": [], - "name": "profileStorage", + "name": "parametersStorage", "outputs": [ { - "internalType": "contract ProfileStorage", + "internalType": "contract ParametersStorage", "name": "", "type": "address" } @@ -734,10 +760,10 @@ }, { "inputs": [], - "name": "proximityScoringProxy", + "name": "profileStorage", "outputs": [ { - "internalType": "contract ProximityScoringProxy", + "internalType": "contract ProfileStorage", "name": "", "type": "address" } @@ -860,6 +886,46 @@ "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": [ { @@ -915,6 +981,46 @@ "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": [ { diff --git a/contracts/v2/CommitManagerV2.sol b/contracts/v2/CommitManagerV1.sol similarity index 81% rename from contracts/v2/CommitManagerV2.sol rename to contracts/v2/CommitManagerV1.sol index ff94e277..9f63e6a5 100644 --- a/contracts/v2/CommitManagerV2.sol +++ b/contracts/v2/CommitManagerV1.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.16; import {HashingProxy} from "../v1/HashingProxy.sol"; +import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; import {LinearSum} from "./scoring/LinearSum.sol"; import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; import {StakingV2} from "./Staking.sol"; @@ -40,13 +41,15 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { 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]; HashingProxy public hashingProxy; + + Log2PLDSF public log2pldsf; LinearSum public linearSum; - ProximityScoringProxy public proximityScoringProxy; StakingV2 public stakingContract; IdentityStorageV2 public identityStorage; ParametersStorage public parametersStorage; @@ -62,8 +65,14 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); - proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); - linearSum = LinearSum(proximityScoringProxy.getScoreFunctionContractAddress(_LINEAR_SUM_ID)); + log2pldsf = Log2PLDSF( + ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress(_LOG2PLDSF_ID) + ); + linearSum = LinearSum( + ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress( + _LINEAR_SUM_ID + ) + ); stakingContract = StakingV2(hub.getContractAddress("Staking")); identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage")); @@ -153,6 +162,72 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { return epochCommits; } + function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + + bytes32 agreementId = hashingProxy.callHashFunction( + args.hashFunctionId, + 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; @@ -209,18 +284,15 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = linearSum.calculateDistance( + LinearSum ls = linearSum; + + uint256 distance = ls.calculateDistance( args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword ); - uint40 score = linearSum.calculateScore( - distance, - maxDistance, - nodesCount, - stakingStorage.totalStakes(identityId) - ); + uint40 score = ls.calculateScore(distance, maxDistance, nodesCount, stakingStorage.totalStakes(identityId)); _insertCommit(agreementId, args.epoch, identityId, 0, 0, score); @@ -250,6 +322,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { ) internal virtual returns (uint72, uint256) { ShardingTableStorageV2 sts = shardingTableStorage; ProfileStorage ps = profileStorage; + LinearSum ls = linearSum; ( ShardingTableStructsV2.Node memory leftEdgeNode, @@ -279,7 +352,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { uint72 rightEdgeNextIdentityId = sts.indexToIdentityId(rightEdgeNodeIndex + 1); uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) : NULL; - (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = linearSum + (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = ls .calculateNeighborhoodBoundaryDistances( hashFunctionId, ps.getNodeId(leftEdgeNode.identityId), @@ -314,45 +387,38 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { // Verify that closestNode is indeed closest if ( - closestDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || - closestDistance > linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) + closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || + closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidClosestNode( agreementId, epoch, closestNodeIndex, closestDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), block.timestamp ); // Verify that leftNode is indeed the left edge of the Neighborhood - if ( - leftEdgeDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)) - ) + if (leftEdgeDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId))) revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, epoch, leftEdgeNodeIndex, leftEdgeDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp ); // Verify that rightNode is indeed the right edge of the Neighborhood - if ( - rightEdgeDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)) - ) + if (rightEdgeDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId))) revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, epoch, rightEdgeNodeIndex, rightEdgeDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp ); diff --git a/contracts/v2/CommitManagerV2U1.sol b/contracts/v2/CommitManagerV1U1.sol similarity index 74% rename from contracts/v2/CommitManagerV2U1.sol rename to contracts/v2/CommitManagerV1U1.sol index d10f1f79..4319b9ba 100644 --- a/contracts/v2/CommitManagerV2U1.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -3,9 +3,11 @@ pragma solidity ^0.8.16; import {HashingProxy} from "../v1/HashingProxy.sol"; +import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; import {LinearSum} from "./scoring/LinearSum.sol"; import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; import {StakingV2} from "./Staking.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"; @@ -54,13 +56,14 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { 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]; HashingProxy public hashingProxy; + Log2PLDSF public log2pldsf; LinearSum public linearSum; - ProximityScoringProxy public proximityScoringProxy; StakingV2 public stakingContract; ContentAssetStorageV2 public contentAssetStorage; IdentityStorageV2 public identityStorage; @@ -76,8 +79,14 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { function initialize() public onlyHubOwner { hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); - proximityScoringProxy = ProximityScoringProxy(hub.getContractAddress("ScoringProxy")); - linearSum = LinearSum(proximityScoringProxy.getScoreFunctionContractAddress(_LINEAR_SUM_ID)); + log2pldsf = Log2PLDSF( + ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress(_LOG2PLDSF_ID) + ); + linearSum = LinearSum( + ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress( + _LINEAR_SUM_ID + ) + ); stakingContract = StakingV2(hub.getContractAddress("Staking")); contentAssetStorage = ContentAssetStorageV2(hub.getAssetStorageAddress("ContentAssetStorage")); identityStorage = IdentityStorageV2(hub.getContractAddress("IdentityStorage")); @@ -196,8 +205,78 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { return epochStateCommits; } + function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { + ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; + Log2PLDSF l2p = log2pldsf; + + bytes32 agreementId = hashingProxy.callHashFunction( + args.hashFunctionId, + 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 = hashingProxy.callHashFunction( args.hashFunctionId, @@ -255,14 +334,8 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = linearSum.calculateDistance( - args.hashFunctionId, - args.keyword, - profileStorage.getNodeId(identityId) - ); - - uint40 score = linearSum.calculateScore( - distance, + uint40 score = ls.calculateScore( + ls.calculateDistance(args.hashFunctionId, args.keyword, profileStorage.getNodeId(identityId)), maxDistance, nodesCount, stakingStorage.totalStakes(identityId) @@ -282,9 +355,11 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ); } - function submitUpdateCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { + 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); @@ -292,7 +367,110 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); + bytes32 agreementId = hashingProxy.callHashFunction( + args.hashFunctionId, + 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 = hashingProxy.callHashFunction( args.hashFunctionId, @@ -348,14 +526,8 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { args.rightEdgeNodeIndex ); - uint256 distance = linearSum.calculateDistance( - args.hashFunctionId, - profileStorage.getNodeId(identityId), - args.keyword - ); - - uint40 score = linearSum.calculateScore( - distance, + uint40 score = ls.calculateScore( + ls.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), maxDistance, nodesCount, stakingStorage.totalStakes(identityId) @@ -420,6 +592,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ) internal virtual returns (uint72, uint256) { ShardingTableStorageV2 sts = shardingTableStorage; ProfileStorage ps = profileStorage; + LinearSum ls = linearSum; ( ShardingTableStructsV2.Node memory leftEdgeNode, @@ -484,45 +657,38 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { // Verify that closestNode is indeed closest if ( - closestDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || - closestDistance > linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) + closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || + closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) ) revert CommitManagerErrorsV2.InvalidClosestNode( agreementId, epoch, closestNodeIndex, closestDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), block.timestamp ); // Verify that leftNode is indeed the left edge of the Neighborhood - if ( - leftEdgeDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)) - ) + if (leftEdgeDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId))) revert CommitManagerErrorsV2.InvalidLeftEdgeNode( agreementId, epoch, leftEdgeNodeIndex, leftEdgeDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp ); // Verify that rightNode is indeed the right edge of the Neighborhood - if ( - rightEdgeDistance > - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)) - ) + if (rightEdgeDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId))) revert CommitManagerErrorsV2.InvalidRightEdgeNode( agreementId, epoch, rightEdgeNodeIndex, rightEdgeDistance, - linearSum.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), + ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp ); diff --git a/deploy/033_deploy_commit_manager_v2.ts b/deploy/031_deploy_commit_manager_v2.ts similarity index 65% rename from deploy/033_deploy_commit_manager_v2.ts rename to deploy/031_deploy_commit_manager_v2.ts index 4891bd5d..c2520d10 100644 --- a/deploy/033_deploy_commit_manager_v2.ts +++ b/deploy/031_deploy_commit_manager_v2.ts @@ -2,13 +2,22 @@ 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('CommitManagerV2', CommitManagerV2); + await hre.helpers.updateContractParameters('CommitManagerV1', CommitManagerV2); }; export default func; diff --git a/deploy/031_deploy_commit_manager_v1.ts b/deploy/032_deploy_commit_manager_v1.ts similarity index 75% rename from deploy/031_deploy_commit_manager_v1.ts rename to deploy/032_deploy_commit_manager_v1.ts index b47a6be9..98714077 100644 --- a/deploy/031_deploy_commit_manager_v1.ts +++ b/deploy/032_deploy_commit_manager_v1.ts @@ -2,7 +2,11 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - if (hre.helpers.isDeployed('CommitManagerV2')) { + if ( + hre.helpers.isDeployed('CommitManagerV1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1'].version?.startsWith('2.')) + ) { return; } diff --git a/deploy/034_deploy_commit_manager_v2_u1.ts b/deploy/033_deploy_commit_manager_v2_u1.ts similarity index 66% rename from deploy/034_deploy_commit_manager_v2_u1.ts rename to deploy/033_deploy_commit_manager_v2_u1.ts index 5bb9a7eb..df07fc50 100644 --- a/deploy/034_deploy_commit_manager_v2_u1.ts +++ b/deploy/033_deploy_commit_manager_v2_u1.ts @@ -2,13 +2,22 @@ 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('CommitManagerV2U1', CommitManagerV2U1); + await hre.helpers.updateContractParameters('CommitManagerV1U1', CommitManagerV2U1); }; export default func; diff --git a/deploy/032_deploy_commit_manager_v1_u1.ts b/deploy/034_deploy_commit_manager_v1_u1.ts similarity index 76% rename from deploy/032_deploy_commit_manager_v1_u1.ts rename to deploy/034_deploy_commit_manager_v1_u1.ts index af113054..a5c4e8af 100644 --- a/deploy/032_deploy_commit_manager_v1_u1.ts +++ b/deploy/034_deploy_commit_manager_v1_u1.ts @@ -2,7 +2,11 @@ import { DeployFunction } from 'hardhat-deploy/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - if (hre.helpers.isDeployed('CommitManagerV2U1')) { + if ( + hre.helpers.isDeployed('CommitManagerV1U1') && + (hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version === undefined || + hre.helpers.contractDeployments.contracts['CommitManagerV1U1'].version?.startsWith('2.')) + ) { return; } diff --git a/deployments/parameters.json b/deployments/parameters.json index 907a6818..f38200e9 100644 --- a/deployments/parameters.json +++ b/deployments/parameters.json @@ -68,74 +68,6 @@ } ] }, - "CommitManagerV2": { - "reqs": [ - { - "desiredValue": false, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, false] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - } - ] - }, - "CommitManagerV2U1": { - "reqs": [ - { - "desiredValue": false, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, false] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - }, - { - "desiredValue": false, - "getterArgs": [4], - "setter": "setReq", - "setterArgs": [4, false] - }, - { - "desiredValue": false, - "getterArgs": [5], - "setter": "setReq", - "setterArgs": [5, false] - } - ] - }, "LinearSum": { "w1": "1", "w2": "1", @@ -298,74 +230,6 @@ } ] }, - "CommitManagerV2": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - } - ] - }, - "CommitManagerV2U1": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": true, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, true] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - }, - { - "desiredValue": false, - "getterArgs": [4], - "setter": "setReq", - "setterArgs": [4, false] - }, - { - "desiredValue": false, - "getterArgs": [5], - "setter": "setReq", - "setterArgs": [5, false] - } - ] - }, "LinearSum": { "w1": "1", "w2": "1", @@ -528,74 +392,6 @@ } ] }, - "CommitManagerV2": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - } - ] - }, - "CommitManagerV2U1": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - }, - { - "desiredValue": false, - "getterArgs": [4], - "setter": "setReq", - "setterArgs": [4, false] - }, - { - "desiredValue": false, - "getterArgs": [5], - "setter": "setReq", - "setterArgs": [5, false] - } - ] - }, "LinearSum": { "w1": "1", "w2": "1", @@ -758,74 +554,6 @@ } ] }, - "CommitManagerV2": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - } - ] - }, - "CommitManagerV2U1": { - "reqs": [ - { - "desiredValue": true, - "getterArgs": [0], - "setter": "setReq", - "setterArgs": [0, true] - }, - { - "desiredValue": false, - "getterArgs": [1], - "setter": "setReq", - "setterArgs": [1, false] - }, - { - "desiredValue": false, - "getterArgs": [2], - "setter": "setReq", - "setterArgs": [2, false] - }, - { - "desiredValue": false, - "getterArgs": [3], - "setter": "setReq", - "setterArgs": [3, false] - }, - { - "desiredValue": false, - "getterArgs": [4], - "setter": "setReq", - "setterArgs": [4, false] - }, - { - "desiredValue": false, - "getterArgs": [5], - "setter": "setReq", - "setterArgs": [5, false] - } - ] - }, "LinearSum": { "w1": "1", "w2": "1", diff --git a/index.ts b/index.ts index 1c0de5e0..8bb7008e 100644 --- a/index.ts +++ b/index.ts @@ -122,10 +122,8 @@ export { ServiceAgreementStorageV1U1 as ServiceAgreementStorageV1U1ABI, ServiceAgreementStorageProxy as ServiceAgreementStorageProxyABI, ServiceAgreementV1 as ServiceAgreementV1ABI, - CommitManagerV1 as CommitManagerV1ABI, - CommitManagerV1U1 as CommitManagerV1U1ABI, - CommitManagerV2 as CommitManagerV2ABI, - CommitManagerV2U1 as CommitManagerV2U1ABI, + CommitManagerV2 as CommitManagerV1ABI, + CommitManagerV2U1 as CommitManagerV1U1ABI, ProofManagerV1 as ProofManagerV1ABI, ProofManagerV1U1 as ProofManagerV1U1ABI, ContentAssetStorageV2 as ContentAssetStorageABI, diff --git a/test/v2/unit/ContentAssetStorageV2.test.ts b/test/v2/unit/ContentAssetStorageV2.test.ts index ff5249cb..e18903b3 100644 --- a/test/v2/unit/ContentAssetStorageV2.test.ts +++ b/test/v2/unit/ContentAssetStorageV2.test.ts @@ -48,8 +48,6 @@ describe('@v2 @unit ContentAssetStorageV2', function () { 'ShardingTableStorageV2', 'ShardingTableV2', 'StakingV2', - 'CommitManagerV1', - 'CommitManagerV1U1', 'CommitManagerV2', 'CommitManagerV2U1', 'ContentAsset', From 8821a175c454acee258f0db4339deff1a5e7f3a8 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 10:09:32 +0100 Subject: [PATCH 30/46] Fixed head() function in the ShardingTableStorageV2 --- contracts/v2/storage/ShardingTableStorage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index 2a04c2c5..e97b53b5 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -59,7 +59,7 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { } function head() external view returns (uint72) { - return nodes[0].identityId; + return indexToIdentityId[0]; } function getHashRingPosition(uint72 identityId) external view returns (uint256) { From 645e3285c4e72a035fc1ee9812afe433fdd2b89b Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 15:19:50 +0100 Subject: [PATCH 31/46] Removed unused parameter from Sharding Table getter --- abi/ShardingTableV2.json | 14 ++------------ contracts/v2/ShardingTable.sol | 14 +++++++------- contracts/v2/structs/ShardingTableStructsV2.sol | 8 -------- deploy/031_deploy_commit_manager_v2.ts | 3 ++- deploy/033_deploy_commit_manager_v2_u1.ts | 3 ++- deploy/034_deploy_commit_manager_v1_u1.ts | 2 +- 6 files changed, 14 insertions(+), 30 deletions(-) diff --git a/abi/ShardingTableV2.json b/abi/ShardingTableV2.json index 4d2dc2a3..476d7bba 100644 --- a/abi/ShardingTableV2.json +++ b/abi/ShardingTableV2.json @@ -108,11 +108,6 @@ "outputs": [ { "components": [ - { - "internalType": "uint256", - "name": "hashRingPosition", - "type": "uint256" - }, { "internalType": "bytes", "name": "nodeId", @@ -134,7 +129,7 @@ "type": "uint96" } ], - "internalType": "struct ShardingTableStructsV2.NodeInfo[]", + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", "name": "", "type": "tuple[]" } @@ -159,11 +154,6 @@ "outputs": [ { "components": [ - { - "internalType": "uint256", - "name": "hashRingPosition", - "type": "uint256" - }, { "internalType": "bytes", "name": "nodeId", @@ -185,7 +175,7 @@ "type": "uint96" } ], - "internalType": "struct ShardingTableStructsV2.NodeInfo[]", + "internalType": "struct ShardingTableStructsV1.NodeInfo[]", "name": "", "type": "tuple[]" } diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index 1d3f6a41..9295ad44 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -9,6 +9,7 @@ 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"; @@ -45,11 +46,11 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { function getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) external view returns (ShardingTableStructsV2.NodeInfo[] memory) { + ) external view returns (ShardingTableStructsV1.NodeInfo[] memory) { return _getShardingTable(startingIdentityId, nodesNumber); } - function getShardingTable() external view returns (ShardingTableStructsV2.NodeInfo[] memory) { + function getShardingTable() external view returns (ShardingTableStructsV1.NodeInfo[] memory) { ShardingTableStorageV2 sts = shardingTableStorage; return _getShardingTable(sts.indexToIdentityId(0), sts.nodesCount()); } @@ -186,8 +187,8 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { function _getShardingTable( uint72 startingIdentityId, uint72 nodesNumber - ) internal view virtual returns (ShardingTableStructsV2.NodeInfo[] memory) { - ShardingTableStructsV2.NodeInfo[] memory nodesPage; + ) internal view virtual returns (ShardingTableStructsV1.NodeInfo[] memory) { + ShardingTableStructsV1.NodeInfo[] memory nodesPage; ShardingTableStorageV2 sts = shardingTableStorage; if ((sts.nodesCount() == 0) || (nodesNumber == 0)) return nodesPage; @@ -196,7 +197,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { require((startingIdentityId == NULL) || (startingNode.identityId != NULL), "Wrong starting Identity ID"); - nodesPage = new ShardingTableStructsV2.NodeInfo[](nodesNumber); + nodesPage = new ShardingTableStructsV1.NodeInfo[](nodesNumber); ProfileStorage ps = profileStorage; StakingStorage ss = stakingStorage; @@ -204,8 +205,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { uint72 nextIdentityId = startingIdentityId; uint72 index; while ((index < nodesNumber) && (nextIdentityId != NULL)) { - nodesPage[index] = ShardingTableStructsV2.NodeInfo({ - hashRingPosition: uint256(ps.getNodeAddress(nextIdentityId, 1)), + nodesPage[index] = ShardingTableStructsV1.NodeInfo({ nodeId: ps.getNodeId(nextIdentityId), identityId: nextIdentityId, ask: ps.getAsk(nextIdentityId), diff --git a/contracts/v2/structs/ShardingTableStructsV2.sol b/contracts/v2/structs/ShardingTableStructsV2.sol index 24c18d6f..50802dc5 100644 --- a/contracts/v2/structs/ShardingTableStructsV2.sol +++ b/contracts/v2/structs/ShardingTableStructsV2.sol @@ -3,14 +3,6 @@ pragma solidity ^0.8.16; library ShardingTableStructsV2 { - struct NodeInfo { - uint256 hashRingPosition; - bytes nodeId; - uint72 identityId; - uint96 ask; - uint96 stake; - } - struct Node { uint256 hashRingPosition; uint72 index; diff --git a/deploy/031_deploy_commit_manager_v2.ts b/deploy/031_deploy_commit_manager_v2.ts index c2520d10..c342e54d 100644 --- a/deploy/031_deploy_commit_manager_v2.ts +++ b/deploy/031_deploy_commit_manager_v2.ts @@ -23,10 +23,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { export default func; func.tags = ['CommitManagerV2', 'v2']; func.dependencies = [ - 'Hub', + 'HubV2', 'IdentityStorageV2', 'ProximityScoringProxy', 'Log2PLDSF', + 'LinearSum', 'ParametersStorage', 'ProfileStorage', 'ServiceAgreementStorageProxy', diff --git a/deploy/033_deploy_commit_manager_v2_u1.ts b/deploy/033_deploy_commit_manager_v2_u1.ts index df07fc50..81f28a89 100644 --- a/deploy/033_deploy_commit_manager_v2_u1.ts +++ b/deploy/033_deploy_commit_manager_v2_u1.ts @@ -23,14 +23,15 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { export default func; func.tags = ['CommitManagerV2U1', 'v2']; func.dependencies = [ - 'ContentAssetStorage', 'HubV2', 'IdentityStorageV2', 'ProximityScoringProxy', 'Log2PLDSF', + 'LinearSum', 'ParametersStorage', 'ProfileStorage', 'ServiceAgreementStorageProxy', + 'ContentAssetStorageV2', 'HashingProxy', 'SHA256', 'ShardingTableStorageV2', diff --git a/deploy/034_deploy_commit_manager_v1_u1.ts b/deploy/034_deploy_commit_manager_v1_u1.ts index a5c4e8af..ea72eb4d 100644 --- a/deploy/034_deploy_commit_manager_v1_u1.ts +++ b/deploy/034_deploy_commit_manager_v1_u1.ts @@ -22,7 +22,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { export default func; func.tags = ['CommitManagerV1U1', 'v1']; func.dependencies = [ - 'ContentAssetStorage', 'Hub', 'IdentityStorage', 'ScoringProxy', @@ -30,6 +29,7 @@ func.dependencies = [ 'ParametersStorage', 'ProfileStorage', 'ServiceAgreementStorageProxy', + 'ContentAssetStorage', 'HashingProxy', 'SHA256', 'ShardingTableStorage', From 940afd112685cfd20aad52f13274b1ce47bff409 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 18:08:41 +0100 Subject: [PATCH 32/46] Added missing parameter to the getter for LinearSum, fixed calculation of the idealNeighborhoodMaxDistance --- abi/LinearSum.json | 9 +++++++-- contracts/v2/scoring/LinearSum.sol | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/abi/LinearSum.json b/abi/LinearSum.json index bcbb7b7b..8f1b0842 100644 --- a/abi/LinearSum.json +++ b/abi/LinearSum.json @@ -159,9 +159,14 @@ "name": "getParameters", "outputs": [ { - "internalType": "uint192", + "internalType": "uint96", "name": "", - "type": "uint192" + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" }, { "internalType": "uint32", diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 374c1fc6..d1c3240a 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -94,7 +94,7 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende 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() / 2); + uint256 idealMaxDistance = (HASH_RING_SIZE / nodesCount) * ((parametersStorage.r2() + 1) / 2); uint256 divisor = (maxDistance <= idealMaxDistance) ? maxDistance : idealMaxDistance; uint256 maxMultiplier = type(uint256).max / distance; @@ -122,8 +122,8 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende return uint64((uint256(stakeScaleFactor) * (stake - minStake)) / (maxStake - minStake)); } - function getParameters() external view returns (uint192, uint32, uint32) { - return (distanceScaleFactor, w1, w2); + function getParameters() external view returns (uint96, uint96, uint32, uint32) { + return (distanceScaleFactor, stakeScaleFactor, w1, w2); } function setDistanceScaleFactor(uint96 distanceScaleFactor_) external onlyHubOwner { From 4c13a9e957bee2991d38f3b477818d6a155c2d4c Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 18:18:07 +0100 Subject: [PATCH 33/46] Deployed new contracts for Devnet environment on Gnosis Chiado --- deployments/gnosis_chiado_dev_contracts.json | 172 ++++++++++--------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/deployments/gnosis_chiado_dev_contracts.json b/deployments/gnosis_chiado_dev_contracts.json index aaf34a1f..3e48f3bf 100644 --- a/deployments/gnosis_chiado_dev_contracts.json +++ b/deployments/gnosis_chiado_dev_contracts.json @@ -2,10 +2,10 @@ "contracts": { "Assertion": { "deployed": true, - "deploymentTimestamp": 1701108252805, - "evmAddress": "0x2785dB031C67B17B07daB7E1e3F9690E9709B735", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634876810, + "evmAddress": "0xa71eBAC52FF4b35e0CdF0D91EA3300493730085C", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.1" }, "AssertionStorage": { @@ -18,26 +18,26 @@ }, "CommitManagerV1": { "deployed": true, - "deploymentTimestamp": 1701108327533, - "evmAddress": "0x94218Ce6A441f027dFB08d0cAEe3A028014A70C2", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "deploymentTimestamp": 1706634939541, + "evmAddress": "0x0DD156b382DBd72CF78185cc7ef6fd119fEae629", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "CommitManagerV1U1": { "deployed": true, - "deploymentTimestamp": 1701108343997, - "evmAddress": "0x223A884F8a4CA10242aAC2b5a5857337764Ff805", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "deploymentTimestamp": 1706634947336, + "evmAddress": "0x847ba87Af72b034b742A9a8bdBA1fD22D07De48d", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "ContentAsset": { "deployed": true, - "deploymentTimestamp": 1701108698623, - "evmAddress": "0xaC93381697288fBBa9C4a7ED555ca57C74f0e85B", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634994134, + "evmAddress": "0x7df62f6aD48f3997044daBB9EDD52c2cB6541143", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.2" }, "ContentAssetStorage": { @@ -50,10 +50,10 @@ }, "HashingProxy": { "deployed": true, - "deploymentTimestamp": 1701108085609, - "evmAddress": "0xD1a30E13DefA65684e22CC09EF51ca12D9c022C3", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634793019, + "evmAddress": "0xD8738563813De59f8A319a51F36D9915D1106Fa4", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.1" }, "Hub": { @@ -74,10 +74,10 @@ }, "Identity": { "deployed": true, - "deploymentTimestamp": 1701108258606, - "evmAddress": "0x654c24F865cd6420bEA563DE61ccf4b1C25C5EC0", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634883250, + "evmAddress": "0x2aCb907aC3150bac1f078bA940d600A120bc0684", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.1" }, "IdentityStorage": { @@ -88,76 +88,84 @@ "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", "version": "2.0.0" }, + "LinearSum": { + "deployed": true, + "deploymentTimestamp": 1706634836074, + "evmAddress": "0x016CA5aEb165E866b77294e2535C0b832979A431", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": null + }, "Log2PLDSF": { "deployed": true, - "deploymentTimestamp": 1701108123571, - "evmAddress": "0x144D304afA78Fd7093c2257695D1633cCEfaf13E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634823448, + "evmAddress": "0xedA13336AeC11eef89780d3842198EFd0C64cE66", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": null }, "ParametersStorage": { "deployed": true, - "deploymentTimestamp": 1701108052661, - "evmAddress": "0x4dD9339bBe3C49CF8806b7440cbc57BFC82b5ad5", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634778058, + "evmAddress": "0x617C690C423fe9649fD7C567a1a5596C54973da0", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.1.0" }, "Profile": { "deployed": true, - "deploymentTimestamp": 1701108296922, - "evmAddress": "0xC7967b6dF41CD6A331D7Bb88129c63b6bb91614e", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634914422, + "evmAddress": "0x270fDc705915359b625c7415F4D4B8ed8AbfD78c", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.2" }, "ProfileStorage": { "deployed": true, - "deploymentTimestamp": 1701108187258, - "evmAddress": "0xD658511427E6B4BeCcea024832F891e398A40343", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634853729, + "evmAddress": "0xaC323Eb986b2901dF6Eb0AA92b32CE19860872D2", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.0" }, "ProofManagerV1": { "deployed": true, - "deploymentTimestamp": 1701108333806, - "evmAddress": "0x1E83a78E12C693d0f2EB94AC51e442E0D1B63a3b", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634960145, + "evmAddress": "0xFa776cE133041C8A02a0722545B35e6f54a930ef", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.2" }, "ProofManagerV1U1": { "deployed": true, - "deploymentTimestamp": 1701108370005, - "evmAddress": "0xD77E59D58a3020f2752B9003151B8dF22588d60F", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634968121, + "evmAddress": "0xF1490D6174b5205955EB2B7cbA53A048537de536", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.0.2" }, "SHA256": { "deployed": true, - "deploymentTimestamp": 1701108091443, - "evmAddress": "0xfE6f7Bb8Dc73b35F14C3bE12bCA424B5DA6C8d94", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634802948, + "evmAddress": "0xbE7e411AAd241c837C33D5E9FA30958Dc78bd358", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": null }, "ScoringProxy": { "deployed": true, - "deploymentTimestamp": 1701108109320, - "evmAddress": "0x57A5FcB27C97d49578d67e2f276A82B7F61e3Af9", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "deploymentTimestamp": 1706634813441, + "evmAddress": "0xC78c8e594BcF088dCD49cf5F940D1C3e7a64c352", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "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": { @@ -178,35 +186,35 @@ }, "ServiceAgreementV1": { "deployed": true, - "deploymentTimestamp": 1701108691794, - "evmAddress": "0x7e37087595f31a15c3BFc325104e3FaE08ac730f", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", + "deploymentTimestamp": 1706634983707, + "evmAddress": "0x79CF1BC4dB73CBCcE57504C88dA1a8F714dDde03", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", "version": "1.1.1" }, "ShardingTable": { "deployed": true, - "deploymentTimestamp": 1701108272713, - "evmAddress": "0xBBd73E52Bc29678c7b4Df53F50162BB0cAc5D7Db", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.1" + "deploymentTimestamp": 1706634893512, + "evmAddress": "0xdCD4748D0a59A144EeDE64463A16F018BbAf5692", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "ShardingTableStorage": { "deployed": true, - "deploymentTimestamp": 1701108162127, - "evmAddress": "0x42232C6A2C4230B56c5DDc3536aaaFa4F6f97E2E", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.0" + "deploymentTimestamp": 1706634843363, + "evmAddress": "0x10a8D6A4cf750A4d5CE41C9672e76B36F412fB8C", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "Staking": { "deployed": true, - "deploymentTimestamp": 1701108286747, - "evmAddress": "0xf73bd80B7B05575DDd79247eEA644E030094a47B", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "1.0.2" + "deploymentTimestamp": 1706634903967, + "evmAddress": "0x653eA41709EC5aE7AbBA3229453E18177c07d98f", + "gitBranch": "release/delegations", + "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", + "version": "2.0.0" }, "StakingStorage": { "deployed": true, From 962dda4d78e60f2d7749140f3cbd7e6c076e4413 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Tue, 30 Jan 2024 21:41:27 +0100 Subject: [PATCH 34/46] Redeployed IdentityStorage on Gnosis Chiado for Devnet environment --- deployments/gnosis_chiado_dev_contracts.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/deployments/gnosis_chiado_dev_contracts.json b/deployments/gnosis_chiado_dev_contracts.json index 3e48f3bf..48bbf1d3 100644 --- a/deployments/gnosis_chiado_dev_contracts.json +++ b/deployments/gnosis_chiado_dev_contracts.json @@ -81,12 +81,12 @@ "version": "1.0.1" }, "IdentityStorage": { - "deployed": true, - "deploymentTimestamp": 1701108151627, - "evmAddress": "0xf0c302F9F7DC143BedF575ad9a3697194FF1bdCf", - "gitBranch": "main", - "gitCommitHash": "83b739de2ea02ce8027e2fccc6e04e9d5da48814", - "version": "2.0.0" + "evmAddress": "0x969cc6855df69C9aEdC6ee94102fD042B4323C2C", + "version": "2.0.0", + "gitBranch": "release/delegations", + "gitCommitHash": "4c13a9e957bee2991d38f3b477818d6a155c2d4c", + "deploymentTimestamp": 1706646689410, + "deployed": true }, "LinearSum": { "deployed": true, From 4ba2bb50a195c0a2ae977316101184511c30c375 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Wed, 31 Jan 2024 15:41:02 +0100 Subject: [PATCH 35/46] Fixed CommitManagerV1/V1U1 bugs, fixed LinearSum score calculation to avoid underflows, added tests for CommitManagerV1 and LinearSum --- abi/CommitManagerV2.json | 10 + abi/CommitManagerV2U1.json | 10 + contracts/v2/CommitManagerV1.sol | 21 +- contracts/v2/CommitManagerV1U1.sol | 21 +- contracts/v2/errors/CommitManagerErrorsV2.sol | 2 + contracts/v2/scoring/LinearSum.sol | 14 +- hardhat.node.config.ts | 4 +- test/v2/unit/CommitManagerV2.test.ts | 493 ++++++++++++++++++ test/v2/unit/LinearSum.test.ts | 40 ++ 9 files changed, 591 insertions(+), 24 deletions(-) create mode 100644 test/v2/unit/CommitManagerV2.test.ts create mode 100644 test/v2/unit/LinearSum.test.ts diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json index fcdd0a12..856de1f7 100644 --- a/abi/CommitManagerV2.json +++ b/abi/CommitManagerV2.json @@ -135,6 +135,11 @@ "name": "leftEdgeNodeIndex", "type": "uint72" }, + { + "internalType": "uint72", + "name": "rightEdgeNodeAdjacentIndex", + "type": "uint72" + }, { "internalType": "uint256", "name": "leftEdgeNodeDistance", @@ -243,6 +248,11 @@ "name": "rightEdgeNodeIndex", "type": "uint72" }, + { + "internalType": "uint72", + "name": "leftEdgeNodeAdjacentIndex", + "type": "uint72" + }, { "internalType": "uint256", "name": "rightEdgeNodeDistance", diff --git a/abi/CommitManagerV2U1.json b/abi/CommitManagerV2U1.json index 661f4b15..d6a0df49 100644 --- a/abi/CommitManagerV2U1.json +++ b/abi/CommitManagerV2U1.json @@ -140,6 +140,11 @@ "name": "leftEdgeNodeIndex", "type": "uint72" }, + { + "internalType": "uint72", + "name": "rightEdgeNodeAdjacentIndex", + "type": "uint72" + }, { "internalType": "uint256", "name": "leftEdgeNodeDistance", @@ -248,6 +253,11 @@ "name": "rightEdgeNodeIndex", "type": "uint72" }, + { + "internalType": "uint72", + "name": "leftEdgeNodeAdjacentIndex", + "type": "uint72" + }, { "internalType": "uint256", "name": "rightEdgeNodeDistance", diff --git a/contracts/v2/CommitManagerV1.sol b/contracts/v2/CommitManagerV1.sol index 9f63e6a5..7c531180 100644 --- a/contracts/v2/CommitManagerV1.sol +++ b/contracts/v2/CommitManagerV1.sol @@ -337,20 +337,19 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); uint72 nodesCount = sts.nodesCount(); - uint72 nodesInBetweenClockwise = ( - (rightEdgeNode.index > leftEdgeNode.index) - ? rightEdgeNode.index - leftEdgeNode.index - 1 - : leftEdgeNode.index - rightEdgeNode.index - 1 - ); - uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) - ? nodesInBetweenClockwise + 2 - : nodesCount - nodesInBetweenClockwise; + 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 = sts.indexToIdentityId(rightEdgeNodeIndex + 1); - uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) : NULL; + 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( @@ -406,6 +405,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { agreementId, epoch, leftEdgeNodeIndex, + rightEdgeNodeIndex != nodesCount - 1 ? rightEdgeNodeIndex + 1 : 0, leftEdgeDistance, ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp @@ -417,6 +417,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { agreementId, epoch, rightEdgeNodeIndex, + leftEdgeNodeIndex != 0 ? leftEdgeNodeIndex - 1 : nodesCount - 1, rightEdgeDistance, ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol index 4319b9ba..b85e6172 100644 --- a/contracts/v2/CommitManagerV1U1.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -607,20 +607,19 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { closestNode.hashRingPosition <= rightEdgeNode.hashRingPosition); uint72 nodesCount = sts.nodesCount(); - uint72 nodesInBetweenClockwise = ( - (rightEdgeNode.index > leftEdgeNode.index) - ? rightEdgeNode.index - leftEdgeNode.index - 1 - : leftEdgeNode.index - rightEdgeNode.index - 1 - ); - uint72 neighborhoodSize = (nodesInBetweenClockwise < nodesCount - 2 - nodesInBetweenClockwise) - ? nodesInBetweenClockwise + 2 - : nodesCount - nodesInBetweenClockwise; + 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 = sts.indexToIdentityId(rightEdgeNodeIndex + 1); - uint72 leftEdgePrevIdentityId = leftEdgeNodeIndex != 0 ? sts.indexToIdentityId(leftEdgeNodeIndex - 1) : NULL; + 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( @@ -676,6 +675,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { agreementId, epoch, leftEdgeNodeIndex, + rightEdgeNodeIndex != nodesCount - 1 ? rightEdgeNodeIndex + 1 : 0, leftEdgeDistance, ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(rightEdgeNextIdentityId)), block.timestamp @@ -687,6 +687,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { agreementId, epoch, rightEdgeNodeIndex, + leftEdgeNodeIndex != 0 ? leftEdgeNodeIndex - 1 : nodesCount - 1, rightEdgeDistance, ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(leftEdgePrevIdentityId)), block.timestamp diff --git a/contracts/v2/errors/CommitManagerErrorsV2.sol b/contracts/v2/errors/CommitManagerErrorsV2.sol index d93f8a20..e11d0a73 100644 --- a/contracts/v2/errors/CommitManagerErrorsV2.sol +++ b/contracts/v2/errors/CommitManagerErrorsV2.sol @@ -34,6 +34,7 @@ library CommitManagerErrorsV2 { bytes32 agreementId, uint16 epoch, uint72 leftEdgeNodeIndex, + uint72 rightEdgeNodeAdjacentIndex, uint256 leftEdgeNodeDistance, uint256 rightEdgeNodeAdjacentDistance, uint256 timeNow @@ -42,6 +43,7 @@ library CommitManagerErrorsV2 { bytes32 agreementId, uint16 epoch, uint72 rightEdgeNodeIndex, + uint72 leftEdgeNodeAdjacentIndex, uint256 rightEdgeNodeDistance, uint256 leftEdgeNodeAdjacentDistance, uint256 timeNow diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index d1c3240a..1a90126d 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -52,8 +52,18 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint72 maxNodesNumber, uint96 stake ) external view returns (uint40) { - return - uint40((1e18 - normalizeDistance(distance, maxDistance, maxNodesNumber)) * w1 + normalizeStake(stake) * w2); + uint64 normalizedDistance = normalizeDistance(distance, maxDistance, maxNodesNumber); + + if (1e18 >= normalizedDistance) { + return uint40((1e18 - normalizedDistance) * w1 + normalizeStake(stake) * w2); + } else { + uint64 proximityScore = (normalizedDistance - 1e18) * w1; + uint64 stakeScore = normalizeStake(stake) * w2; + if (stakeScore > proximityScore) { + return 0; + } + return uint40(proximityScore - stakeScore); + } } function calculateDistance( diff --git a/hardhat.node.config.ts b/hardhat.node.config.ts index d9b15ccd..41afc8b0 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: 300 }, throwOnTransactionFailures: true, throwOnCallFailures: true, loggingEnabled: false, diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts new file mode 100644 index 00000000..04b919bd --- /dev/null +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -0,0 +1,493 @@ +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'; + +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 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); + + 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(3).toString('hex'), + randomBytes(2).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) { + 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; + } + + 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); + } + + if (finalScore.gt(UINT40_MAX_BN)) { + finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); + } + + 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 deployCommitManagerV1Fixture(): 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(deployCommitManagerV1Fixture)); + }); + + 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, + }; + + await expect( + CommitManagerV2['submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))'](commitV2InputArgs), + ).to.emit(CommitManagerV2, 'CommitSubmitted'); + }); + + 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, + }; + + for (const node of neighborhood.slice(0, 3).reverse()) { + await expect( + CommitManagerV2.connect(node.account)[ + 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' + ](commitV2InputArgs), + ).to.emit(CommitManagerV2, 'CommitSubmitted'); + } + + const topCommits = await CommitManagerV2.getTopCommitSubmissions(agreementId, 0); + const scoredNeighborhood = await Promise.all( + neighborhood.map(async (node) => ({ + identityId: node.identityId, + score: ( + await calculateScore( + node.distance, + node.stake, + neighborhood[neighborhood.length - 1].distance, + r2, + nodes.length, + minStake, + maxStake, + ) + ).toNumber(), + })), + ); + const expectedWinners = scoredNeighborhood.sort((a, b) => b.score - a.score).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/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts new file mode 100644 index 00000000..dd2d6f1f --- /dev/null +++ b/test/v2/unit/LinearSum.test.ts @@ -0,0 +1,40 @@ +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { expect } from 'chai'; +import { BigNumber } from 'ethers'; +import hre from 'hardhat'; + +import { LinearSum } from '../../../typechain'; + +type LinearSumFixture = { + accounts: SignerWithAddress[]; + LinearSum: LinearSum; +}; + +describe('@v2 @unit LinearSum', function () { + let accounts: SignerWithAddress[]; + let LinearSum: LinearSum; + + async function deployLinearSumFixture(): Promise { + await hre.deployments.fixture(['LinearSum']); + LinearSum = await hre.ethers.getContract('LinearSum'); + accounts = await hre.ethers.getSigners(); + + return { accounts, LinearSum }; + } + + 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, + ]); + }); +}); From 119c601f8c05b2dbb047944cfcdc961f73332bb4 Mon Sep 17 00:00:00 2001 From: Djordje Kovacevic Date: Wed, 31 Jan 2024 16:23:13 +0100 Subject: [PATCH 36/46] Resolved issue when fetching sharding table --- contracts/v2/ShardingTable.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index 9295ad44..d79d06dc 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -215,7 +215,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { unchecked { index += 1; } - nextIdentityId = sts.indexToIdentityId(index); + nextIdentityId = sts.indexToIdentityId(index + startingNode.index); } return nodesPage; From 9160c83037169f22de3475812412146ca1b58f15 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 1 Feb 2024 01:34:05 +0100 Subject: [PATCH 37/46] Added nodeId to the ShardingTableStorage, removed redundant imports in CommitManagerV2/V2U1, fixed bug in LinearSum score calculation, fixed unit tests for CommitManagerV2 --- abi/CommitManagerV2.json | 26 ------- abi/CommitManagerV2U1.json | 36 ++------- abi/ShardingTableStorageV2.json | 35 +++++++++ contracts/v2/CommitManagerV1.sol | 55 ++++---------- contracts/v2/CommitManagerV1U1.sol | 76 ++++++------------- contracts/v2/ShardingTable.sol | 2 +- contracts/v2/scoring/LinearSum.sol | 4 +- contracts/v2/storage/ShardingTableStorage.sol | 8 +- .../v2/structs/ShardingTableStructsV2.sol | 1 + test/v2/unit/CommitManagerV2.test.ts | 48 +++++++++--- 10 files changed, 125 insertions(+), 166 deletions(-) diff --git a/abi/CommitManagerV2.json b/abi/CommitManagerV2.json index 856de1f7..4b9788b9 100644 --- a/abi/CommitManagerV2.json +++ b/abi/CommitManagerV2.json @@ -513,19 +513,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "hashingProxy", - "outputs": [ - { - "internalType": "contract HashingProxy", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "hub", @@ -724,19 +711,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "stakingContract", - "outputs": [ - { - "internalType": "contract StakingV2", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "stakingStorage", diff --git a/abi/CommitManagerV2U1.json b/abi/CommitManagerV2U1.json index d6a0df49..eb79b779 100644 --- a/abi/CommitManagerV2U1.json +++ b/abi/CommitManagerV2U1.json @@ -453,13 +453,13 @@ "anonymous": false, "inputs": [ { - "indexed": true, + "indexed": false, "internalType": "address", "name": "assetContract", "type": "address" }, { - "indexed": true, + "indexed": false, "internalType": "uint256", "name": "tokenId", "type": "uint256" @@ -489,7 +489,7 @@ "type": "uint256" }, { - "indexed": true, + "indexed": false, "internalType": "uint72", "name": "identityId", "type": "uint72" @@ -508,13 +508,13 @@ "anonymous": false, "inputs": [ { - "indexed": true, + "indexed": false, "internalType": "address", "name": "assetContract", "type": "address" }, { - "indexed": true, + "indexed": false, "internalType": "uint256", "name": "tokenId", "type": "uint256" @@ -617,19 +617,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "hashingProxy", - "outputs": [ - { - "internalType": "contract HashingProxy", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "hub", @@ -857,19 +844,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "stakingContract", - "outputs": [ - { - "internalType": "contract StakingV2", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "stakingStorage", diff --git a/abi/ShardingTableStorageV2.json b/abi/ShardingTableStorageV2.json index c595f88d..31b9d1b1 100644 --- a/abi/ShardingTableStorageV2.json +++ b/abi/ShardingTableStorageV2.json @@ -17,6 +17,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "identityId", @@ -150,6 +155,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", @@ -196,6 +206,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", @@ -218,6 +233,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", @@ -240,6 +260,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", @@ -276,6 +301,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", @@ -312,6 +342,11 @@ "name": "hashRingPosition", "type": "uint256" }, + { + "internalType": "bytes", + "name": "nodeId", + "type": "bytes" + }, { "internalType": "uint72", "name": "index", diff --git a/contracts/v2/CommitManagerV1.sol b/contracts/v2/CommitManagerV1.sol index 7c531180..93e74558 100644 --- a/contracts/v2/CommitManagerV1.sol +++ b/contracts/v2/CommitManagerV1.sol @@ -2,11 +2,8 @@ pragma solidity ^0.8.16; -import {HashingProxy} from "../v1/HashingProxy.sol"; import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; import {LinearSum} from "./scoring/LinearSum.sol"; -import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; -import {StakingV2} from "./Staking.sol"; import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; import {ParametersStorage} from "../v1/storage/ParametersStorage.sol"; import {ProfileStorage} from "../v1/storage/ProfileStorage.sol"; @@ -21,11 +18,8 @@ import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1 import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; import {ShardingTableStructsV2} from "../v2/structs/ShardingTableStructsV2.sol"; import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; -import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; -import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; -import {NULL} from "../v1/constants/ShardingTableConstants.sol"; contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( @@ -46,11 +40,8 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { bool[4] public reqs = [false, false, false, false]; - HashingProxy public hashingProxy; - Log2PLDSF public log2pldsf; LinearSum public linearSum; - StakingV2 public stakingContract; IdentityStorageV2 public identityStorage; ParametersStorage public parametersStorage; ProfileStorage public profileStorage; @@ -64,16 +55,8 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { constructor(address hubAddress) ContractStatus(hubAddress) {} function initialize() public onlyHubOwner { - hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); - log2pldsf = Log2PLDSF( - ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress(_LOG2PLDSF_ID) - ); - linearSum = LinearSum( - ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress( - _LINEAR_SUM_ID - ) - ); - stakingContract = StakingV2(hub.getContractAddress("Staking")); + 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")); @@ -165,10 +148,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function submitCommit(ServiceAgreementStructsV1.CommitInputArgs calldata args) external { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (!sasProxy.agreementV1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -183,7 +163,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); - uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); revert ServiceAgreementErrorsV1.CommitWindowClosed( @@ -231,10 +210,7 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { function submitCommit(ServiceAgreementStructsV2.CommitInputArgs calldata args) external { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (!sasProxy.agreementV1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -249,7 +225,6 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); - uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); revert ServiceAgreementErrorsV1.CommitWindowClosed( @@ -354,9 +329,9 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = ls .calculateNeighborhoodBoundaryDistances( hashFunctionId, - ps.getNodeId(leftEdgeNode.identityId), - ps.getNodeId(closestNode.identityId), - ps.getNodeId(rightEdgeNode.identityId), + leftEdgeNode.nodeId, + closestNode.nodeId, + rightEdgeNode.nodeId, keyword ); @@ -386,40 +361,40 @@ contract CommitManagerV2 is Named, Versioned, ContractStatus, Initializable { // Verify that closestNode is indeed closest if ( - closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || - closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) + 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, keyword, ps.getNodeId(closestPrevIdentityId)), - ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), + 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, keyword, ps.getNodeId(rightEdgeNextIdentityId))) + 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, keyword, ps.getNodeId(rightEdgeNextIdentityId)), + 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, keyword, ps.getNodeId(leftEdgePrevIdentityId))) + 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, keyword, ps.getNodeId(leftEdgePrevIdentityId)), + ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword), block.timestamp ); diff --git a/contracts/v2/CommitManagerV1U1.sol b/contracts/v2/CommitManagerV1U1.sol index b85e6172..99ca4be0 100644 --- a/contracts/v2/CommitManagerV1U1.sol +++ b/contracts/v2/CommitManagerV1U1.sol @@ -2,11 +2,8 @@ pragma solidity ^0.8.16; -import {HashingProxy} from "../v1/HashingProxy.sol"; import {Log2PLDSF} from "../v1/scoring/log2pldsf.sol"; import {LinearSum} from "./scoring/LinearSum.sol"; -import {ProximityScoringProxy} from "./ProximityScoringProxy.sol"; -import {StakingV2} from "./Staking.sol"; import {ContentAssetStorage} from "../v1/storage/assets/ContentAssetStorage.sol"; import {ContentAssetStorageV2} from "./storage/assets/ContentAssetStorage.sol"; import {IdentityStorageV2} from "./storage/IdentityStorage.sol"; @@ -25,27 +22,24 @@ import {ServiceAgreementStructsV1} from "../v1/structs/ServiceAgreementStructsV1 import {ServiceAgreementStructsV2} from "./structs/ServiceAgreementStructsV2.sol"; import {ShardingTableStructsV2} from "./structs/ShardingTableStructsV2.sol"; import {CommitManagerErrorsV2} from "./errors/CommitManagerErrorsV2.sol"; -import {ContentAssetErrors} from "./errors/assets/ContentAssetErrors.sol"; -import {GeneralErrors} from "../v1/errors/GeneralErrors.sol"; import {ServiceAgreementErrorsV1} from "../v1/errors/ServiceAgreementErrorsV1.sol"; import {ServiceAgreementErrorsV1U1} from "../v1/errors/ServiceAgreementErrorsV1U1.sol"; import {ServiceAgreementErrorsV2} from "./errors/ServiceAgreementErrorsV2.sol"; -import {NULL} from "../v1/constants/ShardingTableConstants.sol"; contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { event CommitSubmitted( - address indexed assetContract, - uint256 indexed tokenId, + address assetContract, + uint256 tokenId, bytes keyword, uint8 hashFunctionId, uint16 epoch, uint256 stateIndex, - uint72 indexed identityId, + uint72 identityId, uint40 score ); event StateFinalized( - address indexed assetContract, - uint256 indexed tokenId, + address assetContract, + uint256 tokenId, bytes keyword, uint8 hashFunctionId, uint16 epoch, @@ -61,10 +55,8 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { bool[6] public reqs = [false, false, false, false, false, false]; - HashingProxy public hashingProxy; Log2PLDSF public log2pldsf; LinearSum public linearSum; - StakingV2 public stakingContract; ContentAssetStorageV2 public contentAssetStorage; IdentityStorageV2 public identityStorage; ParametersStorage public parametersStorage; @@ -78,16 +70,8 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { constructor(address hubAddress) ContractStatus(hubAddress) {} function initialize() public onlyHubOwner { - hashingProxy = HashingProxy(hub.getContractAddress("HashingProxy")); - log2pldsf = Log2PLDSF( - ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress(_LOG2PLDSF_ID) - ); - linearSum = LinearSum( - ProximityScoringProxy(hub.getContractAddress("ScoringProxy")).getScoreFunctionContractAddress( - _LINEAR_SUM_ID - ) - ); - stakingContract = StakingV2(hub.getContractAddress("Staking")); + 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")); @@ -209,10 +193,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; Log2PLDSF l2p = log2pldsf; - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -229,7 +210,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); - uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); revert ServiceAgreementErrorsV1U1.CommitWindowClosed( @@ -278,10 +258,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ServiceAgreementStorageProxy sasProxy = serviceAgreementStorageProxy; LinearSum ls = linearSum; - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (sasProxy.agreementV1Exists(agreementId) || !sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -298,7 +275,6 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (!reqs[0] && !isCommitWindowOpen(agreementId, args.epoch)) { uint128 epochLength = sasProxy.getAgreementEpochLength(agreementId); - uint256 actualCommitWindowStart = (sasProxy.getAgreementStartTime(agreementId) + args.epoch * epochLength); revert ServiceAgreementErrorsV1U1.CommitWindowClosed( @@ -335,7 +311,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { ); uint40 score = ls.calculateScore( - ls.calculateDistance(args.hashFunctionId, args.keyword, profileStorage.getNodeId(identityId)), + ls.calculateDistance(args.hashFunctionId, profileStorage.getNodeId(identityId), args.keyword), maxDistance, nodesCount, stakingStorage.totalStakes(identityId) @@ -367,10 +343,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (!sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -472,10 +445,7 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { if (uss.getUnfinalizedState(args.tokenId) == bytes32(0)) revert ServiceAgreementErrorsV1U1.NoPendingUpdate(args.assetContract, args.tokenId); - bytes32 agreementId = hashingProxy.callHashFunction( - args.hashFunctionId, - abi.encodePacked(args.assetContract, args.tokenId, args.keyword) - ); + bytes32 agreementId = sha256(abi.encodePacked(args.assetContract, args.tokenId, args.keyword)); if (!sasProxy.agreementV1U1Exists(agreementId)) revert ServiceAgreementErrorsV1.ServiceAgreementDoesntExist(agreementId); @@ -624,9 +594,9 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { (uint256 leftEdgeDistance, uint256 closestDistance, uint256 rightEdgeDistance) = linearSum .calculateNeighborhoodBoundaryDistances( hashFunctionId, - ps.getNodeId(leftEdgeNode.identityId), - ps.getNodeId(closestNode.identityId), - ps.getNodeId(rightEdgeNode.identityId), + leftEdgeNode.nodeId, + closestNode.nodeId, + rightEdgeNode.nodeId, keyword ); @@ -656,40 +626,40 @@ contract CommitManagerV2U1 is Named, Versioned, ContractStatus, Initializable { // Verify that closestNode is indeed closest if ( - closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestPrevIdentityId)) || - closestDistance > ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)) + 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, keyword, ps.getNodeId(closestPrevIdentityId)), - ls.calculateDistance(hashFunctionId, keyword, ps.getNodeId(closestNextIdentityId)), + 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, keyword, ps.getNodeId(rightEdgeNextIdentityId))) + 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, keyword, ps.getNodeId(rightEdgeNextIdentityId)), + 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, keyword, ps.getNodeId(leftEdgePrevIdentityId))) + 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, keyword, ps.getNodeId(leftEdgePrevIdentityId)), + ls.calculateDistance(hashFunctionId, ps.getNodeId(leftEdgePrevIdentityId), keyword), block.timestamp ); diff --git a/contracts/v2/ShardingTable.sol b/contracts/v2/ShardingTable.sol index 9295ad44..fa37778e 100644 --- a/contracts/v2/ShardingTable.sol +++ b/contracts/v2/ShardingTable.sol @@ -157,7 +157,7 @@ contract ShardingTableV2 is Named, Versioned, ContractStatus, Initializable { ); // Create node object + set index pointer to new identityId + increment total nodes count - sts.createNodeObject(newNodeHashRingPosition, identityId, index); + sts.createNodeObject(newNodeHashRingPosition, ps.getNodeId(identityId), identityId, index); sts.setIdentityId(index, identityId); sts.incrementNodesCount(); diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 1a90126d..6f74785d 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -59,10 +59,10 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende } else { uint64 proximityScore = (normalizedDistance - 1e18) * w1; uint64 stakeScore = normalizeStake(stake) * w2; - if (stakeScore > proximityScore) { + if (stakeScore <= proximityScore) { return 0; } - return uint40(proximityScore - stakeScore); + return uint40(stakeScore - proximityScore); } } diff --git a/contracts/v2/storage/ShardingTableStorage.sol b/contracts/v2/storage/ShardingTableStorage.sol index e97b53b5..ada90cdf 100644 --- a/contracts/v2/storage/ShardingTableStorage.sol +++ b/contracts/v2/storage/ShardingTableStorage.sol @@ -38,10 +38,16 @@ contract ShardingTableStorageV2 is Named, Versioned, HubDependent { nodesCount--; } - function createNodeObject(uint256 hashRingPosition, uint72 identityId, uint72 index) external onlyContracts { + 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 }); } diff --git a/contracts/v2/structs/ShardingTableStructsV2.sol b/contracts/v2/structs/ShardingTableStructsV2.sol index 50802dc5..c6012f3f 100644 --- a/contracts/v2/structs/ShardingTableStructsV2.sol +++ b/contracts/v2/structs/ShardingTableStructsV2.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.16; library ShardingTableStructsV2 { struct Node { uint256 hashRingPosition; + bytes nodeId; uint72 index; uint72 identityId; } diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index 04b919bd..b668feca 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -395,9 +395,26 @@ describe('@v2 @unit CommitManagerV2 contract', function () { 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['submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))'](commitV2InputArgs), - ).to.emit(CommitManagerV2, 'CommitSubmitted'); + 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 () => { @@ -459,17 +476,9 @@ describe('@v2 @unit CommitManagerV2 contract', function () { rightEdgeNodeIndex: rightEdgeNode.index, }; - for (const node of neighborhood.slice(0, 3).reverse()) { - await expect( - CommitManagerV2.connect(node.account)[ - 'submitCommit((address,uint256,bytes,uint8,uint16,uint72,uint72,uint72))' - ](commitV2InputArgs), - ).to.emit(CommitManagerV2, 'CommitSubmitted'); - } - - const topCommits = await CommitManagerV2.getTopCommitSubmissions(agreementId, 0); const scoredNeighborhood = await Promise.all( neighborhood.map(async (node) => ({ + account: node.account, identityId: node.identityId, score: ( await calculateScore( @@ -484,7 +493,22 @@ describe('@v2 @unit CommitManagerV2 contract', function () { ).toNumber(), })), ); - const expectedWinners = scoredNeighborhood.sort((a, b) => b.score - a.score).slice(0, r0); + + 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 })), From 5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 1 Feb 2024 14:44:09 +0100 Subject: [PATCH 38/46] Added NodeOperatorFeeChangesStorage, updated deployment scripts, added unit tests for LinearSum + gas consumption test for ShardingTable --- .github/workflows/checks.yml | 2 +- abi/NodeOperatorFeeChangesStorage.json | 169 ++++++++++++++++++ abi/StakingV2.json | 143 +++++++++++++-- contracts/v2/Staking.sol | 46 ++++- contracts/v2/errors/StakingErrors.sol | 1 + .../storage/NodeOperatorFeeChangesStorage.sol | 51 ++++++ ...eploy_node_operator_fee_changes_storage.ts | 12 ++ ...orage.ts => 018_deploy_profile_storage.ts} | 0 ...19_deploy_service_agreement_storage_v1.ts} | 0 ..._deploy_service_agreement_storage_v1u1.ts} | 0 ...deploy_service_agreement_storage_proxy.ts} | 0 ...=> 022_deploy_content_asset_storage_v2.ts} | 0 ...ts => 023_deploy_content_asset_storage.ts} | 0 ...> 024_deploy_unfinalized_state_storage.ts} | 0 ...y_assertion.ts => 025_deploy_assertion.ts} | 0 ...loy_identity.ts => 026_deploy_identity.ts} | 0 ..._v2.ts => 027_deploy_sharding_table_v2.ts} | 0 ..._table.ts => 028_deploy_sharding_table.ts} | 0 ...staking_v2.ts => 029_deploy_staking_v2.ts} | 1 + ...eploy_staking.ts => 030_deploy_staking.ts} | 0 ...eploy_profile.ts => 031_deploy_profile.ts} | 0 ..._v2.ts => 032_deploy_commit_manager_v2.ts} | 0 ..._v1.ts => 033_deploy_commit_manager_v1.ts} | 0 ....ts => 034_deploy_commit_manager_v2_u1.ts} | 0 ....ts => 035_deploy_commit_manager_v1_u1.ts} | 0 ...r_v1.ts => 036_deploy_proof_manager_v1.ts} | 0 ...1.ts => 037_deploy_proof_manager_v1_u1.ts} | 0 ....ts => 038_deploy_service_agreement_v1.ts} | 0 ...t_asset.ts => 039_deploy_content_asset.ts} | 0 hardhat.node.config.ts | 2 +- index.ts | 3 + package.json | 1 + test/v2/unit/CommitManagerV2.test.ts | 17 +- test/v2/unit/LinearSum.test.ts | 164 ++++++++++++++++- test/v2/unit/ShardingTableV2.test.ts | 81 ++++++++- 35 files changed, 652 insertions(+), 41 deletions(-) create mode 100644 abi/NodeOperatorFeeChangesStorage.json create mode 100644 contracts/v2/storage/NodeOperatorFeeChangesStorage.sol create mode 100644 deploy/017_deploy_node_operator_fee_changes_storage.ts rename deploy/{017_deploy_profile_storage.ts => 018_deploy_profile_storage.ts} (100%) rename deploy/{018_deploy_service_agreement_storage_v1.ts => 019_deploy_service_agreement_storage_v1.ts} (100%) rename deploy/{019_deploy_service_agreement_storage_v1u1.ts => 020_deploy_service_agreement_storage_v1u1.ts} (100%) rename deploy/{020_deploy_service_agreement_storage_proxy.ts => 021_deploy_service_agreement_storage_proxy.ts} (100%) rename deploy/{021_deploy_content_asset_storage_v2.ts => 022_deploy_content_asset_storage_v2.ts} (100%) rename deploy/{022_deploy_content_asset_storage.ts => 023_deploy_content_asset_storage.ts} (100%) rename deploy/{023_deploy_unfinalized_state_storage.ts => 024_deploy_unfinalized_state_storage.ts} (100%) rename deploy/{024_deploy_assertion.ts => 025_deploy_assertion.ts} (100%) rename deploy/{025_deploy_identity.ts => 026_deploy_identity.ts} (100%) rename deploy/{026_deploy_sharding_table_v2.ts => 027_deploy_sharding_table_v2.ts} (100%) rename deploy/{027_deploy_sharding_table.ts => 028_deploy_sharding_table.ts} (100%) rename deploy/{028_deploy_staking_v2.ts => 029_deploy_staking_v2.ts} (96%) rename deploy/{029_deploy_staking.ts => 030_deploy_staking.ts} (100%) rename deploy/{030_deploy_profile.ts => 031_deploy_profile.ts} (100%) rename deploy/{031_deploy_commit_manager_v2.ts => 032_deploy_commit_manager_v2.ts} (100%) rename deploy/{032_deploy_commit_manager_v1.ts => 033_deploy_commit_manager_v1.ts} (100%) rename deploy/{033_deploy_commit_manager_v2_u1.ts => 034_deploy_commit_manager_v2_u1.ts} (100%) rename deploy/{034_deploy_commit_manager_v1_u1.ts => 035_deploy_commit_manager_v1_u1.ts} (100%) rename deploy/{035_deploy_proof_manager_v1.ts => 036_deploy_proof_manager_v1.ts} (100%) rename deploy/{036_deploy_proof_manager_v1_u1.ts => 037_deploy_proof_manager_v1_u1.ts} (100%) rename deploy/{037_deploy_service_agreement_v1.ts => 038_deploy_service_agreement_v1.ts} (100%) rename deploy/{038_deploy_content_asset.ts => 039_deploy_content_asset.ts} (100%) 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/NodeOperatorFeeChangesStorage.json b/abi/NodeOperatorFeeChangesStorage.json new file mode 100644 index 00000000..cacf15fa --- /dev/null +++ b/abi/NodeOperatorFeeChangesStorage.json @@ -0,0 +1,169 @@ +[ + { + "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": [ + { + "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": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/abi/StakingV2.json b/abi/StakingV2.json index ce859ff9..949d7381 100644 --- a/abi/StakingV2.json +++ b/abi/StakingV2.json @@ -37,6 +37,17 @@ "name": "OnlyProfileAdminFunction", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "endTimestamp", + "type": "uint256" + } + ], + "name": "OperatorFeeChangeDelayPending", + "type": "error" + }, { "inputs": [ { @@ -154,7 +165,75 @@ "type": "uint8" } ], - "name": "OperatorFeeUpdated", + "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" }, { @@ -332,6 +411,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint72", + "name": "identityId", + "type": "uint72" + } + ], + "name": "finishOperatorFeeChange", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "hub", @@ -380,10 +472,10 @@ }, { "inputs": [], - "name": "parametersStorage", + "name": "nodeOperatorFeeChangesStorage", "outputs": [ { - "internalType": "contract ParametersStorage", + "internalType": "contract NodeOperatorFeeChangesStorage", "name": "", "type": "address" } @@ -393,10 +485,10 @@ }, { "inputs": [], - "name": "profileStorage", + "name": "parametersStorage", "outputs": [ { - "internalType": "contract ProfileStorage", + "internalType": "contract ParametersStorage", "name": "", "type": "address" } @@ -406,10 +498,10 @@ }, { "inputs": [], - "name": "serviceAgreementStorageProxy", + "name": "profileStorage", "outputs": [ { - "internalType": "contract ServiceAgreementStorageProxy", + "internalType": "contract ProfileStorage", "name": "", "type": "address" } @@ -418,21 +510,16 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "uint72", - "name": "identityId", - "type": "uint72" - }, + "inputs": [], + "name": "serviceAgreementStorageProxy", + "outputs": [ { - "internalType": "uint8", - "name": "operatorFee", - "type": "uint8" + "internalType": "contract ServiceAgreementStorageProxy", + "name": "", + "type": "address" } ], - "name": "setOperatorFee", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -500,6 +587,24 @@ "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": [ { diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index 1bf57e1e..c70605b6 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -5,6 +5,7 @@ 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"; @@ -29,6 +30,13 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint96 oldStake, uint96 newStake ); + event RewardCollected( + uint72 indexed identityId, + bytes nodeId, + address serviceAgreementAddress, + uint96 nodeOperatorFee, + uint96 delegatorsReward + ); event StakeWithdrawalStarted( uint72 indexed identityId, bytes nodeId, @@ -44,13 +52,15 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint96 oldAccumulatedOperatorFee, uint96 newAccumulatedOperatorFee ); - event OperatorFeeUpdated(uint72 indexed identityId, bytes nodeId, uint8 operatorFee); + 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; @@ -69,6 +79,9 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { 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")); @@ -190,6 +203,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { 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 @@ -197,11 +211,33 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { // TBD } - function setOperatorFee(uint72 identityId, uint8 operatorFee) external onlyAdmin(identityId) { - if (operatorFee > 100) revert StakingErrors.InvalidOperatorFee(); - stakingStorage.setOperatorFee(identityId, operatorFee); + function startOperatorFeeChange(uint72 identityId, uint8 newOperatorFee) external onlyAdmin(identityId) { + if (newOperatorFee > 100) revert StakingErrors.InvalidOperatorFee(); + + uint256 feeChangeDelayEnd = block.timestamp + parametersStorage.stakeWithdrawalDelay(); + nodeOperatorFeeChangesStorage.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(feeChangeDelayEnd); + + stakingStorage.setOperatorFee(identityId, newFee); + nofcs.deleteOperatorFeeChangeRequest(identityId); - emit OperatorFeeUpdated(identityId, profileStorage.getNodeId(identityId), operatorFee); + emit OperatorFeeChangeFinished(identityId, profileStorage.getNodeId(identityId), newFee); } function _addStake(address sender, uint72 identityId, uint96 stakeAmount) internal virtual { diff --git a/contracts/v2/errors/StakingErrors.sol b/contracts/v2/errors/StakingErrors.sol index 78f5e00e..7577570b 100644 --- a/contracts/v2/errors/StakingErrors.sol +++ b/contracts/v2/errors/StakingErrors.sol @@ -7,5 +7,6 @@ library StakingErrors { error WithdrawalWasntInitiated(); error WithdrawalPeriodPending(uint256 endTimestamp); error InvalidOperatorFee(); + error OperatorFeeChangeDelayPending(uint256 endTimestamp); error MaximumStakeExceeded(uint256 amount); } diff --git a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol new file mode 100644 index 00000000..a56683da --- /dev/null +++ b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol @@ -0,0 +1,51 @@ +// 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; + } + + // identityId => operatorFeeChangeRequest + mapping(uint72 => OperatorFeeChangeRequest) public operatorFeeChangeRequests; + + // 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 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; + } +} diff --git a/deploy/017_deploy_node_operator_fee_changes_storage.ts b/deploy/017_deploy_node_operator_fee_changes_storage.ts new file mode 100644 index 00000000..874b79e9 --- /dev/null +++ b/deploy/017_deploy_node_operator_fee_changes_storage.ts @@ -0,0 +1,12 @@ +import { DeployFunction } from 'hardhat-deploy/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + await hre.helpers.deploy({ + newContractName: 'NodeOperatorFeeChangesStorage', + }); +}; + +export default func; +func.tags = ['NodeOperatorFeeChangesStorage', 'v2']; +func.dependencies = ['HubV2']; diff --git a/deploy/017_deploy_profile_storage.ts b/deploy/018_deploy_profile_storage.ts similarity index 100% rename from deploy/017_deploy_profile_storage.ts rename to deploy/018_deploy_profile_storage.ts diff --git a/deploy/018_deploy_service_agreement_storage_v1.ts b/deploy/019_deploy_service_agreement_storage_v1.ts similarity index 100% rename from deploy/018_deploy_service_agreement_storage_v1.ts rename to deploy/019_deploy_service_agreement_storage_v1.ts diff --git a/deploy/019_deploy_service_agreement_storage_v1u1.ts b/deploy/020_deploy_service_agreement_storage_v1u1.ts similarity index 100% rename from deploy/019_deploy_service_agreement_storage_v1u1.ts rename to deploy/020_deploy_service_agreement_storage_v1u1.ts diff --git a/deploy/020_deploy_service_agreement_storage_proxy.ts b/deploy/021_deploy_service_agreement_storage_proxy.ts similarity index 100% rename from deploy/020_deploy_service_agreement_storage_proxy.ts rename to deploy/021_deploy_service_agreement_storage_proxy.ts diff --git a/deploy/021_deploy_content_asset_storage_v2.ts b/deploy/022_deploy_content_asset_storage_v2.ts similarity index 100% rename from deploy/021_deploy_content_asset_storage_v2.ts rename to deploy/022_deploy_content_asset_storage_v2.ts diff --git a/deploy/022_deploy_content_asset_storage.ts b/deploy/023_deploy_content_asset_storage.ts similarity index 100% rename from deploy/022_deploy_content_asset_storage.ts rename to deploy/023_deploy_content_asset_storage.ts diff --git a/deploy/023_deploy_unfinalized_state_storage.ts b/deploy/024_deploy_unfinalized_state_storage.ts similarity index 100% rename from deploy/023_deploy_unfinalized_state_storage.ts rename to deploy/024_deploy_unfinalized_state_storage.ts diff --git a/deploy/024_deploy_assertion.ts b/deploy/025_deploy_assertion.ts similarity index 100% rename from deploy/024_deploy_assertion.ts rename to deploy/025_deploy_assertion.ts diff --git a/deploy/025_deploy_identity.ts b/deploy/026_deploy_identity.ts similarity index 100% rename from deploy/025_deploy_identity.ts rename to deploy/026_deploy_identity.ts diff --git a/deploy/026_deploy_sharding_table_v2.ts b/deploy/027_deploy_sharding_table_v2.ts similarity index 100% rename from deploy/026_deploy_sharding_table_v2.ts rename to deploy/027_deploy_sharding_table_v2.ts diff --git a/deploy/027_deploy_sharding_table.ts b/deploy/028_deploy_sharding_table.ts similarity index 100% rename from deploy/027_deploy_sharding_table.ts rename to deploy/028_deploy_sharding_table.ts diff --git a/deploy/028_deploy_staking_v2.ts b/deploy/029_deploy_staking_v2.ts similarity index 96% rename from deploy/028_deploy_staking_v2.ts rename to deploy/029_deploy_staking_v2.ts index fb83dc4e..8cd1bdf7 100644 --- a/deploy/028_deploy_staking_v2.ts +++ b/deploy/029_deploy_staking_v2.ts @@ -29,4 +29,5 @@ func.dependencies = [ 'ServiceAgreementStorageProxy', 'ShardingTableStorageV2', 'StakingStorage', + 'NodeOperatorFeeChangesStorage', ]; diff --git a/deploy/029_deploy_staking.ts b/deploy/030_deploy_staking.ts similarity index 100% rename from deploy/029_deploy_staking.ts rename to deploy/030_deploy_staking.ts diff --git a/deploy/030_deploy_profile.ts b/deploy/031_deploy_profile.ts similarity index 100% rename from deploy/030_deploy_profile.ts rename to deploy/031_deploy_profile.ts diff --git a/deploy/031_deploy_commit_manager_v2.ts b/deploy/032_deploy_commit_manager_v2.ts similarity index 100% rename from deploy/031_deploy_commit_manager_v2.ts rename to deploy/032_deploy_commit_manager_v2.ts diff --git a/deploy/032_deploy_commit_manager_v1.ts b/deploy/033_deploy_commit_manager_v1.ts similarity index 100% rename from deploy/032_deploy_commit_manager_v1.ts rename to deploy/033_deploy_commit_manager_v1.ts diff --git a/deploy/033_deploy_commit_manager_v2_u1.ts b/deploy/034_deploy_commit_manager_v2_u1.ts similarity index 100% rename from deploy/033_deploy_commit_manager_v2_u1.ts rename to deploy/034_deploy_commit_manager_v2_u1.ts diff --git a/deploy/034_deploy_commit_manager_v1_u1.ts b/deploy/035_deploy_commit_manager_v1_u1.ts similarity index 100% rename from deploy/034_deploy_commit_manager_v1_u1.ts rename to deploy/035_deploy_commit_manager_v1_u1.ts diff --git a/deploy/035_deploy_proof_manager_v1.ts b/deploy/036_deploy_proof_manager_v1.ts similarity index 100% rename from deploy/035_deploy_proof_manager_v1.ts rename to deploy/036_deploy_proof_manager_v1.ts diff --git a/deploy/036_deploy_proof_manager_v1_u1.ts b/deploy/037_deploy_proof_manager_v1_u1.ts similarity index 100% rename from deploy/036_deploy_proof_manager_v1_u1.ts rename to deploy/037_deploy_proof_manager_v1_u1.ts diff --git a/deploy/037_deploy_service_agreement_v1.ts b/deploy/038_deploy_service_agreement_v1.ts similarity index 100% rename from deploy/037_deploy_service_agreement_v1.ts rename to deploy/038_deploy_service_agreement_v1.ts diff --git a/deploy/038_deploy_content_asset.ts b/deploy/039_deploy_content_asset.ts similarity index 100% rename from deploy/038_deploy_content_asset.ts rename to deploy/039_deploy_content_asset.ts diff --git a/hardhat.node.config.ts b/hardhat.node.config.ts index 41afc8b0..5e8da7d2 100644 --- a/hardhat.node.config.ts +++ b/hardhat.node.config.ts @@ -30,7 +30,7 @@ const config: HardhatUserConfig = { gasMultiplier: 1, blockGasLimit: 30_000_000, hardfork: 'shanghai', - accounts: { count: 300 }, + accounts: { count: 200 }, throwOnTransactionFailures: true, throwOnCallFailures: true, loggingEnabled: false, diff --git a/index.ts b/index.ts index 8bb7008e..e5b25ce9 100644 --- a/index.ts +++ b/index.ts @@ -27,6 +27,7 @@ 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'; @@ -102,6 +103,7 @@ const ABIV2 = { IdentityStorageV2, CommitManagerV2, CommitManagerV2U1, + NodeOperatorFeeChangesStorage, ShardingTableV2, ShardingTableStorageV2, StakingV2, @@ -133,6 +135,7 @@ export { Identity as IdentityABI, LinearSum as LinearSumABI, Log2PLDSF as Log2PLDSFABI, + NodeOperatorFeeChangesStorage as NodeOperatorFeeChangesStorageABI, ProfileStorage as ProfileStorageABI, Profile as ProfileABI, StakingStorage as StakingStorageABI, diff --git a/package.json b/package.json index 274eee74..91123df2 100644 --- a/package.json +++ b/package.json @@ -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/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index b668feca..c98e28b9 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -23,10 +23,6 @@ import { ContentAssetStructs } from '../../../typechain/contracts/v1/assets/Cont import { ServiceAgreementStructsV1 } from '../../../typechain/contracts/v1/CommitManagerV1'; import { ServiceAgreementStructsV2 } from '../../../typechain/contracts/v2/CommitManagerV2'; -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 CommitManagerV2Fixture = { accounts: SignerWithAddress[]; CommitManagerV2: CommitManagerV2; @@ -51,7 +47,10 @@ type NodeWithDistance = { }; describe('@v2 @unit CommitManagerV2 contract', function () { - const HASH_RING_SIZE = BigNumber.from(2).pow(256); + 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; @@ -108,8 +107,8 @@ describe('@v2 @unit CommitManagerV2 contract', function () { await OperationalProfile.createProfile( admin.address, nodeId, + randomBytes(5).toString('hex'), randomBytes(3).toString('hex'), - randomBytes(2).toString('hex'), ) ).wait(); const identityId = Number(receipt.logs[0].topics[1]); @@ -138,7 +137,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { }; } - async function createMultipleProfiles(count = 150) { + async function createMultipleProfiles(count = 150): Promise { const nodes = []; for (let i = 0; i < count; i++) { @@ -287,7 +286,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { }; } - async function deployCommitManagerV1Fixture(): Promise { + async function deployCommitManagerV2Fixture(): Promise { await hre.deployments.fixture([ 'HubV2', 'ContentAssetStorageV2', @@ -315,7 +314,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { beforeEach(async () => { hre.helpers.resetDeploymentsJson(); - ({ accounts, CommitManagerV2 } = await loadFixture(deployCommitManagerV1Fixture)); + ({ accounts, CommitManagerV2 } = await loadFixture(deployCommitManagerV2Fixture)); }); it('The contract is named "CommitManagerV1"', async () => { diff --git a/test/v2/unit/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts index dd2d6f1f..de6f692d 100644 --- a/test/v2/unit/LinearSum.test.ts +++ b/test/v2/unit/LinearSum.test.ts @@ -1,10 +1,12 @@ +import { randomBytes } from 'crypto'; + import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { expect } from 'chai'; -import { BigNumber } from 'ethers'; +import { BigNumber, BytesLike } from 'ethers'; import hre from 'hardhat'; -import { LinearSum } from '../../../typechain'; +import { LinearSum, ParametersStorage } from '../../../typechain'; type LinearSumFixture = { accounts: SignerWithAddress[]; @@ -12,17 +14,112 @@ type LinearSumFixture = { }; 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; + } + + 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.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)); + } + + 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); + } + + if (finalScore.gt(UINT40_MAX_BN)) { + finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); + } + + return finalScore; + } + beforeEach(async function () { hre.helpers.resetDeploymentsJson(); ({ accounts, LinearSum } = await loadFixture(deployLinearSumFixture)); @@ -37,4 +134,67 @@ describe('@v2 @unit LinearSum', function () { 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); + } + } + }); }); diff --git a/test/v2/unit/ShardingTableV2.test.ts b/test/v2/unit/ShardingTableV2.test.ts index e974956c..28a2c939 100644 --- a/test/v2/unit/ShardingTableV2.test.ts +++ b/test/v2/unit/ShardingTableV2.test.ts @@ -1,9 +1,12 @@ +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, ShardingTableStorageV2, ShardingTableV2 } from '../../../typechain'; +import { HubController, Profile, ProfileStorage, ShardingTableStorageV2, ShardingTableV2 } from '../../../typechain'; type ShardingTableFixture = { accounts: SignerWithAddress[]; @@ -12,11 +15,19 @@ type ShardingTableFixture = { 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'; @@ -36,11 +47,52 @@ describe('@v2 @unit ShardingTableV2 contract', function () { 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]); @@ -66,7 +118,7 @@ describe('@v2 @unit ShardingTableV2 contract', function () { async function validateShardingTableResult(identityIds: number[]) { const nodesCount = (await ShardingTableStorage.nodesCount()).toNumber(); - expect(identityIds.length, 'Invalid number of nodes').to.equal(nodesCount); + 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]); @@ -74,9 +126,9 @@ describe('@v2 @unit ShardingTableV2 contract', function () { expect(node).to.be.eql(nodeByIndex); - expect(node.identityId.toNumber(), 'Invalid node on this position').to.equal(identityIds[i]); + expect(node.identityId.toNumber()).to.equal(identityIds[i], 'Invalid node on this position'); - expect(node.index.toNumber(), 'Invalid node index').to.equal(i); + expect(node.index.toNumber()).to.equal(i, 'Invalid node index'); } const shardingTable = (await ShardingTable['getShardingTable()']()).map((node) => node.identityId.toNumber()); @@ -145,6 +197,27 @@ describe('@v2 @unit ShardingTableV2 contract', function () { 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(); From feceafd7fedf94ffec450f5252bb30a4aac9f994 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Thu, 1 Feb 2024 15:52:02 +0100 Subject: [PATCH 39/46] Redeployed contracts on Gnosis Chiado for Devnet environment --- deployments/gnosis_chiado_dev_contracts.json | 216 ++++++++++--------- hardhat.config.ts | 1 + 2 files changed, 113 insertions(+), 104 deletions(-) diff --git a/deployments/gnosis_chiado_dev_contracts.json b/deployments/gnosis_chiado_dev_contracts.json index 48bbf1d3..ea75051e 100644 --- a/deployments/gnosis_chiado_dev_contracts.json +++ b/deployments/gnosis_chiado_dev_contracts.json @@ -1,12 +1,12 @@ { "contracts": { "Assertion": { - "deployed": true, - "deploymentTimestamp": 1706634876810, - "evmAddress": "0xa71eBAC52FF4b35e0CdF0D91EA3300493730085C", + "evmAddress": "0x3310E685a5471e9824d28AEeAce65d61F5e53698", + "version": "1.0.1", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.1" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798862801, + "deployed": true }, "AssertionStorage": { "deployed": true, @@ -17,28 +17,28 @@ "version": "1.0.0" }, "CommitManagerV1": { - "deployed": true, - "deploymentTimestamp": 1706634939541, - "evmAddress": "0x0DD156b382DBd72CF78185cc7ef6fd119fEae629", + "evmAddress": "0xD8934457A0dFF97b059E3370AEd30F280838f1E6", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798918693, + "deployed": true }, "CommitManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1706634947336, - "evmAddress": "0x847ba87Af72b034b742A9a8bdBA1fD22D07De48d", + "evmAddress": "0x786a9494a031D6221Ad4246fDcFf1dbA0CDb48fE", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798926032, + "deployed": true }, "ContentAsset": { - "deployed": true, - "deploymentTimestamp": 1706634994134, - "evmAddress": "0x7df62f6aD48f3997044daBB9EDD52c2cB6541143", + "evmAddress": "0x0B4bf3f3c83c0d44368d3da175d47BB582EE19ad", + "version": "1.0.2", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.2" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798985649, + "deployed": true }, "ContentAssetStorage": { "deployed": true, @@ -49,12 +49,12 @@ "version": "2.0.0" }, "HashingProxy": { - "deployed": true, - "deploymentTimestamp": 1706634793019, - "evmAddress": "0xD8738563813De59f8A319a51F36D9915D1106Fa4", + "evmAddress": "0x222403e49651Fe40Fa914627AEEF89CDF7040c39", + "version": "1.0.1", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.1" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798758112, + "deployed": true }, "Hub": { "deployed": true, @@ -73,92 +73,92 @@ "version": "1.0.1" }, "Identity": { - "deployed": true, - "deploymentTimestamp": 1706634883250, - "evmAddress": "0x2aCb907aC3150bac1f078bA940d600A120bc0684", + "evmAddress": "0xB7203c97A4d9a9482fa741d2Ce81d5E7B7B64701", + "version": "1.0.1", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.1" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798873457, + "deployed": true }, "IdentityStorage": { - "evmAddress": "0x969cc6855df69C9aEdC6ee94102fD042B4323C2C", + "evmAddress": "0x935baC9BF1F6a593676d84123790646DC9987377", "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "4c13a9e957bee2991d38f3b477818d6a155c2d4c", - "deploymentTimestamp": 1706646689410, + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798809311, "deployed": true }, "LinearSum": { - "deployed": true, - "deploymentTimestamp": 1706634836074, - "evmAddress": "0x016CA5aEb165E866b77294e2535C0b832979A431", + "evmAddress": "0xf3B574a736B11C28B94796BfBEe19b5fb7D66dCe", + "version": null, "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": null + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798797798, + "deployed": true }, "Log2PLDSF": { - "deployed": true, - "deploymentTimestamp": 1706634823448, - "evmAddress": "0xedA13336AeC11eef89780d3842198EFd0C64cE66", + "evmAddress": "0x456E9ad2c7DE41a206d8f3d4C55790666e3438D8", + "version": null, "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": null + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798780791, + "deployed": true }, "ParametersStorage": { - "deployed": true, - "deploymentTimestamp": 1706634778058, - "evmAddress": "0x617C690C423fe9649fD7C567a1a5596C54973da0", + "evmAddress": "0x95637cdd9b5a9442343Bd77c1A8be4a9090AdB1e", + "version": "1.1.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.1.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798743324, + "deployed": true }, "Profile": { - "deployed": true, - "deploymentTimestamp": 1706634914422, - "evmAddress": "0x270fDc705915359b625c7415F4D4B8ed8AbfD78c", + "evmAddress": "0x1C0245DC80fe287B5Ac6a6a1E85ec230a56cc5ea", + "version": "1.0.2", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.2" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798903277, + "deployed": true }, "ProfileStorage": { - "deployed": true, - "deploymentTimestamp": 1706634853729, - "evmAddress": "0xaC323Eb986b2901dF6Eb0AA92b32CE19860872D2", + "evmAddress": "0xe70Fd17F76Eed679Ca8302f059ACf5551694f2fe", + "version": "1.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798853108, + "deployed": true }, "ProofManagerV1": { - "deployed": true, - "deploymentTimestamp": 1706634960145, - "evmAddress": "0xFa776cE133041C8A02a0722545B35e6f54a930ef", + "evmAddress": "0xac1C4Df4B96A84d31d004Bd77DCc1ace853bbfdd", + "version": "1.0.2", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.2" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798933283, + "deployed": true }, "ProofManagerV1U1": { - "deployed": true, - "deploymentTimestamp": 1706634968121, - "evmAddress": "0xF1490D6174b5205955EB2B7cbA53A048537de536", + "evmAddress": "0xaaDd33785d8bc2317910a010CFDf0792e4f54A83", + "version": "1.0.2", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.0.2" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798952557, + "deployed": true }, "SHA256": { - "deployed": true, - "deploymentTimestamp": 1706634802948, - "evmAddress": "0xbE7e411AAd241c837C33D5E9FA30958Dc78bd358", + "evmAddress": "0x00c419eb97fCd4bBe0a9730A629b842288C52E33", + "version": null, "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": null + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798764437, + "deployed": true }, "ScoringProxy": { - "deployed": true, - "deploymentTimestamp": 1706634813441, - "evmAddress": "0xC78c8e594BcF088dCD49cf5F940D1C3e7a64c352", + "evmAddress": "0x55203C4220650Ee63F544a0EB2Fad8a773319576", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798775189, + "deployed": true }, "ServiceAgreementStorageProxy": { "deployed": true, @@ -185,44 +185,44 @@ "version": "1.0.0" }, "ServiceAgreementV1": { - "deployed": true, - "deploymentTimestamp": 1706634983707, - "evmAddress": "0x79CF1BC4dB73CBCcE57504C88dA1a8F714dDde03", + "evmAddress": "0x7C9176EBA46Be9AB324474598e2788DEEcE11A7F", + "version": "1.1.1", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "1.1.1" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798967276, + "deployed": true }, "ShardingTable": { - "deployed": true, - "deploymentTimestamp": 1706634893512, - "evmAddress": "0xdCD4748D0a59A144EeDE64463A16F018BbAf5692", + "evmAddress": "0x05d9631984af59791E81e2c8213D095181c58d26", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798879374, + "deployed": true }, "ShardingTableStorage": { - "deployed": true, - "deploymentTimestamp": 1706634843363, - "evmAddress": "0x10a8D6A4cf750A4d5CE41C9672e76B36F412fB8C", + "evmAddress": "0x9E55a883EB23135D04348CaF7fF5d57A9B06a176", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", + "deploymentTimestamp": 1706798819331, + "deployed": true }, "Staking": { - "deployed": true, - "deploymentTimestamp": 1706634903967, - "evmAddress": "0x653eA41709EC5aE7AbBA3229453E18177c07d98f", + "evmAddress": "0xE4B3cf42BF9F4D796768deFa5B572C527dFe8C7f", + "version": "2.0.0", "gitBranch": "release/delegations", - "gitCommitHash": "940afd112685cfd20aad52f13274b1ce47bff409", - "version": "2.0.0" + "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, @@ -243,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/hardhat.config.ts b/hardhat.config.ts index be9d7d92..1c39f33f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -100,6 +100,7 @@ config.mocha = { reporterOptions: { excludeContracts: [], }, + timeout: 100000000, }; config.abiExporter = { From afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 2 Feb 2024 03:11:47 +0100 Subject: [PATCH 40/46] Added new events and unit tests for CommitManagerV2U1/StakingV2 --- abi/Profile.json | 6 + abi/StakingV2.json | 72 ++ contracts/v1/Profile.sol | 4 +- contracts/v2/Staking.sol | 20 +- contracts/v2/errors/StakingErrors.sol | 4 +- test/v1/unit/Profile.test.ts | 7 +- test/v2/unit/CommitManagerV2U1.test.ts | 871 +++++++++++++++++++++++++ test/v2/unit/StakingV2.test.ts | 533 +++++++++++++++ 8 files changed, 1508 insertions(+), 9 deletions(-) create mode 100644 test/v2/unit/CommitManagerV2U1.test.ts create mode 100644 test/v2/unit/StakingV2.test.ts 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/StakingV2.json b/abi/StakingV2.json index 949d7381..a16a9665 100644 --- a/abi/StakingV2.json +++ b/abi/StakingV2.json @@ -39,6 +39,11 @@ }, { "inputs": [ + { + "internalType": "uint256", + "name": "nowTimestamp", + "type": "uint256" + }, { "internalType": "uint256", "name": "endTimestamp", @@ -93,6 +98,11 @@ }, { "inputs": [ + { + "internalType": "uint256", + "name": "nowTimestamp", + "type": "uint256" + }, { "internalType": "uint256", "name": "endTimestamp", @@ -236,6 +246,68 @@ "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": [ diff --git a/contracts/v1/Profile.sol b/contracts/v1/Profile.sol index d41011a5..1b5f321d 100644 --- a/contracts/v1/Profile.sol +++ b/contracts/v1/Profile.sol @@ -18,7 +18,7 @@ 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); @@ -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) { diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index c70605b6..b57b8588 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -30,6 +30,12 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint96 oldStake, uint96 newStake ); + event SharesMinted( + address indexed sharesContractAddress, + address indexed delegator, + uint256 sharesMintedAmount, + uint256 newTotalSupply + ); event RewardCollected( uint72 indexed identityId, bytes nodeId, @@ -46,6 +52,12 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { 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, @@ -144,6 +156,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { newStake, withdrawalPeriodEnd ); + emit SharesBurned(address(sharesContract), msg.sender, sharesToBurn, sharesContract.totalSupply()); } function withdrawStake(uint72 identityId) external { @@ -158,7 +171,8 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { (stakeWithdrawalAmount, withdrawalTimestamp) = ss.withdrawalRequests(identityId, msg.sender); if (stakeWithdrawalAmount == 0) revert StakingErrors.WithdrawalWasntInitiated(); - if (withdrawalTimestamp >= block.timestamp) revert StakingErrors.WithdrawalPeriodPending(withdrawalTimestamp); + if (block.timestamp < withdrawalTimestamp) + revert StakingErrors.WithdrawalPeriodPending(block.timestamp, withdrawalTimestamp); ss.deleteWithdrawalRequest(identityId, msg.sender); ss.transferStake(msg.sender, stakeWithdrawalAmount); @@ -232,7 +246,8 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { uint256 feeChangeDelayEnd; (newFee, feeChangeDelayEnd) = nofcs.operatorFeeChangeRequests(identityId); - if (block.timestamp < feeChangeDelayEnd) revert StakingErrors.OperatorFeeChangeDelayPending(feeChangeDelayEnd); + if (block.timestamp < feeChangeDelayEnd) + revert StakingErrors.OperatorFeeChangeDelayPending(block.timestamp, feeChangeDelayEnd); stakingStorage.setOperatorFee(identityId, newFee); nofcs.deleteOperatorFeeChangeRequest(identityId); @@ -269,6 +284,7 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { 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 { diff --git a/contracts/v2/errors/StakingErrors.sol b/contracts/v2/errors/StakingErrors.sol index 7577570b..ebd3b984 100644 --- a/contracts/v2/errors/StakingErrors.sol +++ b/contracts/v2/errors/StakingErrors.sol @@ -5,8 +5,8 @@ pragma solidity ^0.8.16; library StakingErrors { error ZeroSharesAmount(); error WithdrawalWasntInitiated(); - error WithdrawalPeriodPending(uint256 endTimestamp); + error WithdrawalPeriodPending(uint256 nowTimestamp, uint256 endTimestamp); error InvalidOperatorFee(); - error OperatorFeeChangeDelayPending(uint256 endTimestamp); + error OperatorFeeChangeDelayPending(uint256 nowTimestamp, uint256 endTimestamp); error MaximumStakeExceeded(uint256 amount); } diff --git a/test/v1/unit/Profile.test.ts b/test/v1/unit/Profile.test.ts index 7c18d62c..747f8e82 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 { diff --git a/test/v2/unit/CommitManagerV2U1.test.ts b/test/v2/unit/CommitManagerV2U1.test.ts new file mode 100644 index 00000000..f4e2d94e --- /dev/null +++ b/test/v2/unit/CommitManagerV2U1.test.ts @@ -0,0 +1,871 @@ +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; + } + + 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); + } + + if (finalScore.gt(UINT40_MAX_BN)) { + finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); + } + + 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/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); + } + }); +}); From de9ecf852c09abbbe95acca80d10a9a284e6a626 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 2 Feb 2024 11:24:12 +0100 Subject: [PATCH 41/46] Redeployed contracts on Gnosis Chiado for Testnet environment --- deployments/gnosis_chiado_test_contracts.json | 244 ++++++++++-------- deployments/parameters.json | 12 +- 2 files changed, 136 insertions(+), 120 deletions(-) diff --git a/deployments/gnosis_chiado_test_contracts.json b/deployments/gnosis_chiado_test_contracts.json index a88f6109..8abc72df 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": "0x0F92191B2bd24Ff7Fb4C65a73a47A8ef6352AE0d", + "version": null, + "gitBranch": "release/delegations", + "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", + "deploymentTimestamp": 1706868323217, + "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 f38200e9..0db5c951 100644 --- a/deployments/parameters.json +++ b/deployments/parameters.json @@ -99,9 +99,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { @@ -261,9 +261,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "300", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "300", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "600" }, "ProofManagerV1": { @@ -423,9 +423,9 @@ "r1": "8", "r2": "20", "replacementWindowDurationPerc": "0", - "rewardWithdrawalDelay": "86400", + "rewardWithdrawalDelay": "60", "slashingFreezeDuration": "63072000", - "stakeWithdrawalDelay": "86400", + "stakeWithdrawalDelay": "60", "updateCommitWindowDuration": "1800" }, "ProofManagerV1": { From 4f38101d106bace102a1bd45afe04ea0c5add4b3 Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 2 Feb 2024 13:36:59 +0100 Subject: [PATCH 42/46] Added Stake balancing for score calculations, added possibility to once set the delay free period for operator fees --- abi/NodeOperatorFeeChangesStorage.json | 26 +++++++++++++++++++ contracts/v1/Profile.sol | 2 +- contracts/v1/scoring/log2pldsf.sol | 9 ++++--- contracts/v1/storage/ParametersStorage.sol | 2 +- contracts/v2/Staking.sol | 8 ++++-- contracts/v2/scoring/LinearSum.sol | 3 ++- .../storage/NodeOperatorFeeChangesStorage.sol | 13 ++++++++++ test/v1/unit/ParametersStorage.test.ts | 4 +-- test/v1/unit/Profile.test.ts | 4 +-- 9 files changed, 59 insertions(+), 12 deletions(-) diff --git a/abi/NodeOperatorFeeChangesStorage.json b/abi/NodeOperatorFeeChangesStorage.json index cacf15fa..9933535b 100644 --- a/abi/NodeOperatorFeeChangesStorage.json +++ b/abi/NodeOperatorFeeChangesStorage.json @@ -33,6 +33,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "delayFreePeriodEnd", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -153,6 +166,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "setDelayFreePeriodEnd", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "version", diff --git a/contracts/v1/Profile.sol b/contracts/v1/Profile.sol index 1b5f321d..0ec0ae10 100644 --- a/contracts/v1/Profile.sol +++ b/contracts/v1/Profile.sol @@ -23,7 +23,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable { 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; 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 dee76b96..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 diff --git a/contracts/v2/Staking.sol b/contracts/v2/Staking.sol index b57b8588..c7d1a3c0 100644 --- a/contracts/v2/Staking.sol +++ b/contracts/v2/Staking.sol @@ -228,8 +228,12 @@ contract StakingV2 is Named, Versioned, ContractStatus, Initializable { function startOperatorFeeChange(uint72 identityId, uint8 newOperatorFee) external onlyAdmin(identityId) { if (newOperatorFee > 100) revert StakingErrors.InvalidOperatorFee(); - uint256 feeChangeDelayEnd = block.timestamp + parametersStorage.stakeWithdrawalDelay(); - nodeOperatorFeeChangesStorage.createOperatorFeeChangeRequest(identityId, newOperatorFee, feeChangeDelayEnd); + NodeOperatorFeeChangesStorage nofcs = nodeOperatorFeeChangesStorage; + + uint256 feeChangeDelayEnd = block.timestamp > nofcs.delayFreePeriodEnd() + ? block.timestamp + parametersStorage.stakeWithdrawalDelay() + : block.timestamp; + nofcs.createOperatorFeeChangeRequest(identityId, newOperatorFee, feeChangeDelayEnd); emit OperatorFeeChangeStarted( identityId, diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 6f74785d..e7c52a80 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -128,8 +128,9 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint96 minStake = ps.minimumStake(); uint96 maxStake = ps.maximumStake(); + uint96 balancedStake = stake <= maxStake ? stake : maxStake; - return uint64((uint256(stakeScaleFactor) * (stake - minStake)) / (maxStake - minStake)); + return uint64((uint256(stakeScaleFactor) * (balancedStake - minStake)) / (maxStake - minStake)); } function getParameters() external view returns (uint96, uint96, uint32, uint32) { diff --git a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol index a56683da..4aa83479 100644 --- a/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol +++ b/contracts/v2/storage/NodeOperatorFeeChangesStorage.sol @@ -15,12 +15,21 @@ contract NodeOperatorFeeChangesStorage is Named, Versioned, HubDependent { 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; } @@ -48,4 +57,8 @@ contract NodeOperatorFeeChangesStorage is Named, Versioned, HubDependent { 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/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 747f8e82..3bed8777 100644 --- a/test/v1/unit/Profile.test.ts +++ b/test/v1/unit/Profile.test.ts @@ -57,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 () => { From 4eb67001cbe597dc7b1fb34ecf495d809c3308a8 Mon Sep 17 00:00:00 2001 From: NZT48 Date: Fri, 2 Feb 2024 16:31:36 +0100 Subject: [PATCH 43/46] Implement scaling down the score --- contracts/v2/scoring/LinearSum.sol | 6 ++++-- contracts/v2/utils/ScaleDownLibrary.sol | 9 +++++++++ test/v2/unit/CommitManagerV2.test.ts | 7 ++++++- test/v2/unit/LinearSum.test.ts | 7 ++++++- 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 contracts/v2/utils/ScaleDownLibrary.sol diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index e7c52a80..420ed79f 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -9,6 +9,7 @@ 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); @@ -55,14 +56,15 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende uint64 normalizedDistance = normalizeDistance(distance, maxDistance, maxNodesNumber); if (1e18 >= normalizedDistance) { - return uint40((1e18 - normalizedDistance) * w1 + normalizeStake(stake) * w2); + return + ScaleDownLib.toUint40((1e18 - normalizedDistance) * w1 + normalizeStake(stake) * w2, type(uint64).max); } else { uint64 proximityScore = (normalizedDistance - 1e18) * w1; uint64 stakeScore = normalizeStake(stake) * w2; if (stakeScore <= proximityScore) { return 0; } - return uint40(stakeScore - proximityScore); + return ScaleDownLib.toUint40(stakeScore - proximityScore, type(uint64).max); } } 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/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index c98e28b9..945cae7f 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -160,6 +160,11 @@ describe('@v2 @unit CommitManagerV2 contract', function () { 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, @@ -220,7 +225,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { } if (finalScore.gt(UINT40_MAX_BN)) { - finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); + finalScore = toUint40(finalScore, UINT64_MAX_BN); } return finalScore; diff --git a/test/v2/unit/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts index de6f692d..5b2164eb 100644 --- a/test/v2/unit/LinearSum.test.ts +++ b/test/v2/unit/LinearSum.test.ts @@ -54,6 +54,11 @@ describe('@v2 @unit LinearSum', function () { 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, @@ -114,7 +119,7 @@ describe('@v2 @unit LinearSum', function () { } if (finalScore.gt(UINT40_MAX_BN)) { - finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); + finalScore = toUint40(finalScore, UINT64_MAX_BN); } return finalScore; From 9458ae72ffe3b81d5f2a07ffaaaaca0310b1419c Mon Sep 17 00:00:00 2001 From: NZT48 Date: Fri, 2 Feb 2024 16:51:37 +0100 Subject: [PATCH 44/46] Fix calculate score in tests --- test/v2/unit/CommitManagerV2.test.ts | 4 +--- test/v2/unit/LinearSum.test.ts | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index 945cae7f..10e25195 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -224,9 +224,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { finalScore = BigNumber.from(0); } - if (finalScore.gt(UINT40_MAX_BN)) { - finalScore = toUint40(finalScore, UINT64_MAX_BN); - } + finalScore = toUint40(finalScore, UINT64_MAX_BN); return finalScore; } diff --git a/test/v2/unit/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts index 5b2164eb..cddeed53 100644 --- a/test/v2/unit/LinearSum.test.ts +++ b/test/v2/unit/LinearSum.test.ts @@ -117,10 +117,7 @@ describe('@v2 @unit LinearSum', function () { } else { finalScore = BigNumber.from(0); } - - if (finalScore.gt(UINT40_MAX_BN)) { - finalScore = toUint40(finalScore, UINT64_MAX_BN); - } + finalScore = toUint40(finalScore, UINT64_MAX_BN); return finalScore; } From 8a1b78347baf5e94f670e9f80b37a10bacc6a496 Mon Sep 17 00:00:00 2001 From: NZT48 Date: Fri, 2 Feb 2024 18:27:19 +0100 Subject: [PATCH 45/46] Implement value verification test for linear sum --- contracts/v2/scoring/LinearSum.sol | 4 +- test/v2/unit/LinearSum.test.ts | 145 +++++++++++++++++++++++++++-- 2 files changed, 138 insertions(+), 11 deletions(-) diff --git a/contracts/v2/scoring/LinearSum.sol b/contracts/v2/scoring/LinearSum.sol index 420ed79f..ad5736a7 100644 --- a/contracts/v2/scoring/LinearSum.sol +++ b/contracts/v2/scoring/LinearSum.sol @@ -57,14 +57,14 @@ contract LinearSum is IProximityScoreFunctionsPair, Indexable, Named, HubDepende if (1e18 >= normalizedDistance) { return - ScaleDownLib.toUint40((1e18 - normalizedDistance) * w1 + normalizeStake(stake) * w2, type(uint64).max); + 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, type(uint64).max); + return ScaleDownLib.toUint40(stakeScore - proximityScore, (w1 + w2) * 1e18); } } diff --git a/test/v2/unit/LinearSum.test.ts b/test/v2/unit/LinearSum.test.ts index cddeed53..0f55a82c 100644 --- a/test/v2/unit/LinearSum.test.ts +++ b/test/v2/unit/LinearSum.test.ts @@ -7,6 +7,7 @@ import { BigNumber, BytesLike } from 'ethers'; import hre from 'hardhat'; import { LinearSum, ParametersStorage } from '../../../typechain'; +import { ZERO_BYTES32 } from '../../helpers/constants'; type LinearSumFixture = { accounts: SignerWithAddress[]; @@ -59,17 +60,15 @@ describe('@v2 @unit LinearSum', function () { return result; } - async function calculateScore( + async function calculateNormalizedDistance( 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 [distanceScaleFactor] = linearSumParams; const idealMaxDistanceInNeighborhood = HASH_RING_SIZE.div(nodesNumber).mul(Math.ceil(r2 / 2)); @@ -95,10 +94,26 @@ describe('@v2 @unit LinearSum', function () { 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)); - } + 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'); @@ -107,9 +122,63 @@ describe('@v2 @unit LinearSum', function () { 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)) { @@ -117,7 +186,32 @@ describe('@v2 @unit LinearSum', function () { } else { finalScore = BigNumber.from(0); } - finalScore = toUint40(finalScore, UINT64_MAX_BN); + + 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; } @@ -199,4 +293,37 @@ describe('@v2 @unit LinearSum', function () { } } }); + + 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')); + }); }); From ee8f51954e1015cdf879df8461cf6df2e563972e Mon Sep 17 00:00:00 2001 From: Uladzislau Hubar Date: Fri, 2 Feb 2024 19:03:07 +0100 Subject: [PATCH 46/46] Fixed unit tests, redeployed LinearSum on Gnosis Chiado for Devnet/Testnet environments --- deployments/gnosis_chiado_dev_contracts.json | 6 +++--- deployments/gnosis_chiado_test_contracts.json | 6 +++--- test/v2/unit/CommitManagerV2.test.ts | 2 +- test/v2/unit/CommitManagerV2U1.test.ts | 9 ++++++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/deployments/gnosis_chiado_dev_contracts.json b/deployments/gnosis_chiado_dev_contracts.json index ea75051e..279d7261 100644 --- a/deployments/gnosis_chiado_dev_contracts.json +++ b/deployments/gnosis_chiado_dev_contracts.json @@ -89,11 +89,11 @@ "deployed": true }, "LinearSum": { - "evmAddress": "0xf3B574a736B11C28B94796BfBEe19b5fb7D66dCe", + "evmAddress": "0x50c8Bcea7935242b991934Eb0251ff0CdAD4ED7f", "version": null, "gitBranch": "release/delegations", - "gitCommitHash": "5fcbdfba99d8b8b9d23d9e2277f73a1bfa70c50c", - "deploymentTimestamp": 1706798797798, + "gitCommitHash": "3170df6307d65977d66d8bc22134ecaf5fef8fe5", + "deploymentTimestamp": 1706896901040, "deployed": true }, "Log2PLDSF": { diff --git a/deployments/gnosis_chiado_test_contracts.json b/deployments/gnosis_chiado_test_contracts.json index 8abc72df..f221be7f 100644 --- a/deployments/gnosis_chiado_test_contracts.json +++ b/deployments/gnosis_chiado_test_contracts.json @@ -237,11 +237,11 @@ "version": "1.0.0" }, "LinearSum": { - "evmAddress": "0x0F92191B2bd24Ff7Fb4C65a73a47A8ef6352AE0d", + "evmAddress": "0x669C7e51EbBdA5C57503Ff19feb463109874C2D5", "version": null, "gitBranch": "release/delegations", - "gitCommitHash": "afbe3c06bae4a20e2a9d902edd197bf8d9d1bdee", - "deploymentTimestamp": 1706868323217, + "gitCommitHash": "3170df6307d65977d66d8bc22134ecaf5fef8fe5", + "deploymentTimestamp": 1706896932563, "deployed": true }, "NodeOperatorFeeChangesStorage": { diff --git a/test/v2/unit/CommitManagerV2.test.ts b/test/v2/unit/CommitManagerV2.test.ts index 10e25195..9239bc9f 100644 --- a/test/v2/unit/CommitManagerV2.test.ts +++ b/test/v2/unit/CommitManagerV2.test.ts @@ -224,7 +224,7 @@ describe('@v2 @unit CommitManagerV2 contract', function () { finalScore = BigNumber.from(0); } - finalScore = toUint40(finalScore, UINT64_MAX_BN); + finalScore = toUint40(finalScore, oneEther.mul(w1 + w2)); return finalScore; } diff --git a/test/v2/unit/CommitManagerV2U1.test.ts b/test/v2/unit/CommitManagerV2U1.test.ts index f4e2d94e..c1b9b860 100644 --- a/test/v2/unit/CommitManagerV2U1.test.ts +++ b/test/v2/unit/CommitManagerV2U1.test.ts @@ -283,6 +283,11 @@ describe('@v2 @unit CommitManagerV2U1 contract', function () { 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, @@ -342,9 +347,7 @@ describe('@v2 @unit CommitManagerV2U1 contract', function () { finalScore = BigNumber.from(0); } - if (finalScore.gt(UINT40_MAX_BN)) { - finalScore = finalScore.mod(UINT40_MAX_BN.add(1)); - } + finalScore = toUint40(finalScore, oneEther.mul(w1 + w2)); return finalScore; }