Skip to content

Commit 2758c75

Browse files
authored
Merge pull request #446 from OriginTrail/improvement/parameters-storage-access-policy
Update ParametersStorage access control
2 parents c24ce55 + f68ea87 commit 2758c75

10 files changed

Lines changed: 112 additions & 101 deletions

abi/ParametersStorage.json

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,6 @@
1515
"stateMutability": "nonpayable",
1616
"type": "constructor"
1717
},
18-
{
19-
"inputs": [
20-
{
21-
"internalType": "string",
22-
"name": "msg",
23-
"type": "string"
24-
}
25-
],
26-
"name": "UnauthorizedAccess",
27-
"type": "error"
28-
},
2918
{
3019
"inputs": [],
3120
"name": "ZeroAddressHub",

contracts/RandomSampling.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,12 @@ contract RandomSampling is INamed, IVersioned, ContractStatus, IInitializable {
443443
// 3. Node publishing factor calculation
444444
// Original: nodeStakeFactor * (nodePublishingFactor / MAX(allNodesPublishingFactors))
445445
uint256 maxNodePub = uint256(epochStorage.getCurrentEpochNodeMaxProducedKnowledgeValue());
446-
if (maxNodePub == 0) {
447-
return nodeStakeFactor18 + nodeAskFactor18;
446+
uint256 nodePublishingFactor18 = 0;
447+
if (maxNodePub > 0) {
448+
uint256 nodePub = uint256(epochStorage.getNodeCurrentEpochProducedKnowledgeValue(identityId));
449+
uint256 pubRatio18 = (nodePub * SCALE18) / maxNodePub;
450+
nodePublishingFactor18 = (nodeStakeFactor18 * pubRatio18) / SCALE18;
448451
}
449-
uint256 nodePub = uint256(epochStorage.getNodeCurrentEpochProducedKnowledgeValue(identityId));
450-
uint256 pubRatio18 = (nodePub * SCALE18) / maxNodePub;
451-
uint256 nodePublishingFactor18 = (nodeStakeFactor18 * pubRatio18) / SCALE18;
452452

453453
return nodeStakeFactor18 + nodeAskFactor18 / 10 + nodePublishingFactor18 * 15;
454454
}

contracts/storage/ParametersStorage.sol

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pragma solidity ^0.8.20;
55
import {HubDependent} from "../abstract/HubDependent.sol";
66
import {INamed} from "../interfaces/INamed.sol";
77
import {IVersioned} from "../interfaces/IVersioned.sol";
8+
import {ICustodian} from "../interfaces/ICustodian.sol";
89

910
contract ParametersStorage is INamed, IVersioned, HubDependent {
1011
event ParameterChanged(string parameterName, uint256 parameterValue);
@@ -31,9 +32,15 @@ contract ParametersStorage is INamed, IVersioned, HubDependent {
3132

3233
uint256 public v81ReleaseEpoch;
3334

35+
// @dev Only transactions by HubController owner or one of the owners of the MultiSig Wallet
36+
modifier onlyOwnerOrMultiSigOwner() {
37+
_checkOwnerOrMultiSigOwner();
38+
_;
39+
}
40+
3441
constructor(address hubAddress, uint256 _v81ReleaseEpoch) HubDependent(hubAddress) {
3542
minimumStake = 50_000 ether;
36-
maximumStake = 2_000_000 ether;
43+
maximumStake = 5_000_000 ether;
3744

3845
stakeWithdrawalDelay = 28 days;
3946
nodeAskUpdateDelay = 1 days;
@@ -62,71 +69,103 @@ contract ParametersStorage is INamed, IVersioned, HubDependent {
6269
return _VERSION;
6370
}
6471

65-
function setAskUpperBoundFactor(uint256 _askUpperBoundFactor) external onlyHub {
72+
function setAskUpperBoundFactor(uint256 _askUpperBoundFactor) external onlyOwnerOrMultiSigOwner {
6673
askUpperBoundFactor = _askUpperBoundFactor;
6774
}
6875

69-
function setAskLowerBoundFactor(uint256 _askLowerBoundFactor) external onlyHub {
76+
function setAskLowerBoundFactor(uint256 _askLowerBoundFactor) external onlyOwnerOrMultiSigOwner {
7077
askLowerBoundFactor = _askLowerBoundFactor;
7178
}
7279

73-
function setMinimumRequiredSignatures(uint256 _minimumRequiredSignatures) external onlyHub {
80+
function setMinimumRequiredSignatures(uint256 _minimumRequiredSignatures) external onlyOwnerOrMultiSigOwner {
7481
minimumRequiredSignatures = _minimumRequiredSignatures;
7582

7683
emit ParameterChanged("minimumRequiredSignatures", _minimumRequiredSignatures);
7784
}
7885

79-
function setMinimumStake(uint96 newMinimumStake) external onlyHub {
86+
function setMinimumStake(uint96 newMinimumStake) external onlyOwnerOrMultiSigOwner {
8087
minimumStake = newMinimumStake;
8188

8289
emit ParameterChanged("minimumStake", newMinimumStake);
8390
}
8491

85-
function setMaximumStake(uint96 newMaximumStake) external onlyHub {
92+
function setMaximumStake(uint96 newMaximumStake) external onlyOwnerOrMultiSigOwner {
8693
maximumStake = newMaximumStake;
8794

8895
emit ParameterChanged("maximumStake", newMaximumStake);
8996
}
9097

91-
function setStakeWithdrawalDelay(uint256 newStakeWithdrawalDelay) external onlyHub {
98+
function setStakeWithdrawalDelay(uint256 newStakeWithdrawalDelay) external onlyOwnerOrMultiSigOwner {
9299
stakeWithdrawalDelay = newStakeWithdrawalDelay;
93100

94101
emit ParameterChanged("stakeWithdrawalDelay", newStakeWithdrawalDelay);
95102
}
96103

97-
function setNodeAskUpdateDelay(uint256 newNodeAskUpdateDelay) external onlyHub {
104+
function setNodeAskUpdateDelay(uint256 newNodeAskUpdateDelay) external onlyOwnerOrMultiSigOwner {
98105
nodeAskUpdateDelay = newNodeAskUpdateDelay;
99106

100107
emit ParameterChanged("nodeAskUpdateDelay", newNodeAskUpdateDelay);
101108
}
102109

103-
function setOperatorFeeUpdateDelay(uint256 newOperatorFeeUpdateDelay) external onlyHub {
110+
function setOperatorFeeUpdateDelay(uint256 newOperatorFeeUpdateDelay) external onlyOwnerOrMultiSigOwner {
104111
operatorFeeUpdateDelay = newOperatorFeeUpdateDelay;
105112

106113
emit ParameterChanged("operatorFeeUpdateDelay", newOperatorFeeUpdateDelay);
107114
}
108115

109-
function setOpWalletsLimitOnProfileCreation(uint16 opWalletsLimitOnProfileCreation_) external onlyHub {
116+
function setOpWalletsLimitOnProfileCreation(
117+
uint16 opWalletsLimitOnProfileCreation_
118+
) external onlyOwnerOrMultiSigOwner {
110119
opWalletsLimitOnProfileCreation = opWalletsLimitOnProfileCreation_;
111120

112121
emit ParameterChanged("opWalletsLimitOnProfileCreation", opWalletsLimitOnProfileCreation);
113122
}
114123

115-
function setShardingTableSizeLimit(uint16 shardingTableSizeLimit_) external onlyHub {
124+
function setShardingTableSizeLimit(uint16 shardingTableSizeLimit_) external onlyOwnerOrMultiSigOwner {
116125
shardingTableSizeLimit = shardingTableSizeLimit_;
117126

118127
emit ParameterChanged("shardingTableSizeLimit", shardingTableSizeLimit);
119128
}
120129

121-
function setMaxOperatorFee(uint16 maxOperatorFee_) external onlyHub {
130+
function setMaxOperatorFee(uint16 maxOperatorFee_) external onlyOwnerOrMultiSigOwner {
122131
maxOperatorFee = maxOperatorFee_;
123132

124133
emit ParameterChanged("maxOperatorFee", maxOperatorFee);
125134
}
126135

127-
function setV81ReleaseEpoch(uint256 _v81ReleaseEpoch) external onlyHub {
136+
function setV81ReleaseEpoch(uint256 _v81ReleaseEpoch) external onlyOwnerOrMultiSigOwner {
128137
v81ReleaseEpoch = _v81ReleaseEpoch;
129138

130139
emit ParameterChanged("v81ReleaseEpoch", _v81ReleaseEpoch);
131140
}
141+
142+
function _isMultiSigOwner(address multiSigAddress) internal view returns (bool) {
143+
// First check if the address has contract code
144+
uint256 size;
145+
assembly {
146+
size := extcodesize(multiSigAddress)
147+
}
148+
149+
// If no contract code, it's an EOA, not a multisig
150+
if (size == 0) {
151+
return false;
152+
}
153+
154+
try ICustodian(multiSigAddress).getOwners() returns (address[] memory multiSigOwners) {
155+
for (uint256 i = 0; i < multiSigOwners.length; i++) {
156+
if (msg.sender == multiSigOwners[i]) {
157+
return true;
158+
}
159+
} // solhint-disable-next-line no-empty-blocks
160+
} catch {}
161+
162+
return false;
163+
}
164+
165+
function _checkOwnerOrMultiSigOwner() internal view virtual {
166+
address hubOwner = hub.owner();
167+
if (msg.sender != hubOwner && msg.sender != address(hub) && !_isMultiSigOwner(hubOwner)) {
168+
revert("Only Hub Owner, Hub, or Multisig Owner can call");
169+
}
170+
}
132171
}

deployments/base_sepolia_test_contracts.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
"deployed": true
2121
},
2222
"ParametersStorage": {
23-
"evmAddress": "0x464c1da3B250984338EfcAFa36Bbc58e80Ff3e36",
23+
"evmAddress": "0x7ad79B9A2607Fd15c4fEf85412684B83473C7dBa",
2424
"version": "1.0.0",
25-
"gitBranch": "main",
26-
"gitCommitHash": "0a60162a01dace5e6f414efa34d7ac62168349ba",
27-
"deploymentBlock": 28140031,
28-
"deploymentTimestamp": 1752048354414,
25+
"gitBranch": "improvement/parameters-storage-access-policy",
26+
"gitCommitHash": "ca8b8ffaaf1961fa270fb7a1a5f187fcc5d45ff4",
27+
"deploymentBlock": 31132834,
28+
"deploymentTimestamp": 1758033973092,
2929
"deployed": true
3030
},
3131
"WhitelistStorage": {
@@ -281,12 +281,12 @@
281281
"deployed": true
282282
},
283283
"RandomSampling": {
284-
"evmAddress": "0x598F34aa6C65433B54C68c866A854A6dc5764DBe",
284+
"evmAddress": "0x09553F8aDACf76D76Da49678555240cb614c36AF",
285285
"version": "1.0.0",
286-
"gitBranch": "release/adjust-score-formula",
287-
"gitCommitHash": "1088476e13c3c8b46f087f45cdea103959f2135c",
288-
"deploymentBlock": 30956460,
289-
"deploymentTimestamp": 1757681208307,
286+
"gitBranch": "improvement/parameters-storage-access-policy",
287+
"gitCommitHash": "ca8b8ffaaf1961fa270fb7a1a5f187fcc5d45ff4",
288+
"deploymentBlock": 31132846,
289+
"deploymentTimestamp": 1758033980703,
290290
"deployed": true
291291
},
292292
"StakingKPI": {

deployments/gnosis_chiado_test_contracts.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
"deployed": true
2121
},
2222
"ParametersStorage": {
23-
"evmAddress": "0x1ECaf623cb31B15d52b57C8965f0ec32E544de12",
23+
"evmAddress": "0x4a652D864148f0e533247Fcfb7B7dC92c7B55e6e",
2424
"version": "1.0.0",
25-
"gitBranch": "main",
26-
"gitCommitHash": "0a60162a01dace5e6f414efa34d7ac62168349ba",
27-
"deploymentBlock": 16660886,
28-
"deploymentTimestamp": 1752048401910,
25+
"gitBranch": "improvement/parameters-storage-access-policy",
26+
"gitCommitHash": "88b220af8f4ff70118a7eb1574d60458cf318b0f",
27+
"deploymentBlock": 17817446,
28+
"deploymentTimestamp": 1758033679341,
2929
"deployed": true
3030
},
3131
"WhitelistStorage": {
@@ -281,12 +281,12 @@
281281
"deployed": true
282282
},
283283
"RandomSampling": {
284-
"evmAddress": "0x017b5C10E68ACb3ca186793841B717192E6493d4",
284+
"evmAddress": "0x3B39e292c3EC9088ceEf6629663857a679249ac4",
285285
"version": "1.0.0",
286-
"gitBranch": "release/adjust-score-formula",
287-
"gitCommitHash": "1088476e13c3c8b46f087f45cdea103959f2135c",
288-
"deploymentBlock": 17749977,
289-
"deploymentTimestamp": 1757681177674,
286+
"gitBranch": "improvement/parameters-storage-access-policy",
287+
"gitCommitHash": "88b220af8f4ff70118a7eb1574d60458cf318b0f",
288+
"deploymentBlock": 17817448,
289+
"deploymentTimestamp": 1758033688668,
290290
"deployed": true
291291
},
292292
"StakingKPI": {

deployments/neuroweb_testnet_contracts.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
"deployed": true
2323
},
2424
"ParametersStorage": {
25-
"evmAddress": "0x598F34aa6C65433B54C68c866A854A6dc5764DBe",
26-
"substrateAddress": "5EMjscze7xSDypZjUWbD743f1bTodFkrjQemysnWD7R2AALa",
25+
"evmAddress": "0xe4fF01a0cEC8172BC5f087655cC8a54aCFB1FDb1",
26+
"substrateAddress": "5EMjsd184SS8WZBgsU9Xyc6WmESaGe7dUwuAqijTV196kz9z",
2727
"version": "1.0.0",
28-
"gitBranch": "main",
29-
"gitCommitHash": "0a60162a01dace5e6f414efa34d7ac62168349ba",
30-
"deploymentBlock": 8256035,
31-
"deploymentTimestamp": 1752048424349,
28+
"gitBranch": "improvement/parameters-storage-access-policy",
29+
"gitCommitHash": "da6617b493c439a5d23a429290c267efc2bd5810",
30+
"deploymentBlock": 9105590,
31+
"deploymentTimestamp": 1758033268884,
3232
"deployed": true
3333
},
3434
"WhitelistStorage": {
@@ -312,13 +312,13 @@
312312
"deployed": true
313313
},
314314
"RandomSampling": {
315-
"evmAddress": "0x3B39e292c3EC9088ceEf6629663857a679249ac4",
316-
"substrateAddress": "5EMjsczY3S8zN14hCEDhUcyFm8JFYDHiugfo6j629Mm52j7i",
315+
"evmAddress": "0x069aE2A26f319B3B758b4E971E5D097f6A8Da1a8",
316+
"substrateAddress": "5EMjsczMVtdpeuuRM4sFzRod4CBDTcnV66XFgacxUNpGFDL2",
317317
"version": "1.0.0",
318-
"gitBranch": "release/adjust-score-formula",
319-
"gitCommitHash": "1088476e13c3c8b46f087f45cdea103959f2135c",
320-
"deploymentBlock": 9057704,
321-
"deploymentTimestamp": 1757682397829,
318+
"gitBranch": "improvement/parameters-storage-access-policy",
319+
"gitCommitHash": "bcad38c0a01f8de58fb773c1ad16634547b7cd16",
320+
"deploymentBlock": 9105222,
321+
"deploymentTimestamp": 1758030544348,
322322
"deployed": true
323323
},
324324
"StakingKPI": {

deployments/parameters.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"development": {
33
"ParametersStorage": {
4-
"maximumStake": "2000000000000000000000000",
4+
"maximumStake": "5000000000000000000000000",
55
"minimumStake": "50000000000000000000000",
66
"stakeWithdrawalDelay": "60",
77
"nodeAskUpdateDelay": "60",
@@ -35,7 +35,7 @@
3535
"devnet": {
3636
"overrides": {},
3737
"ParametersStorage": {
38-
"maximumStake": "2000000000000000000000000",
38+
"maximumStake": "5000000000000000000000000",
3939
"minimumStake": "50000000000000000000000",
4040
"stakeWithdrawalDelay": "60",
4141
"nodeAskUpdateDelay": "60",
@@ -74,7 +74,7 @@
7474
"testnet": {
7575
"overrides": {},
7676
"ParametersStorage": {
77-
"maximumStake": "2000000000000000000000000",
77+
"maximumStake": "5000000000000000000000000",
7878
"minimumStake": "50000000000000000000000",
7979
"stakeWithdrawalDelay": "60",
8080
"nodeAskUpdateDelay": "60",
@@ -114,7 +114,7 @@
114114
"mainnet": {
115115
"overrides": {},
116116
"ParametersStorage": {
117-
"maximumStake": "2000000000000000000000000",
117+
"maximumStake": "5000000000000000000000000",
118118
"minimumStake": "50000000000000000000000",
119119
"stakeWithdrawalDelay": "2419200",
120120
"nodeAskUpdateDelay": "86400",

test/integration/RandomSampling.test.ts

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ async function calculateExpectedNodeScore(
135135
nodePublishingFactor = (nodeStakeFactor * pubRatio) / SCALING_FACTOR;
136136
}
137137

138-
return nodeStakeFactor + nodePublishingFactor + nodeAskFactor;
138+
return nodeStakeFactor + nodeAskFactor / 10n + nodePublishingFactor * 15n;
139139
}
140140

141141
describe('@integration RandomSampling', () => {
@@ -2346,7 +2346,6 @@ describe('@integration RandomSampling', () => {
23462346
it('Should handle precision loss testing and maintain accuracy', async () => {
23472347
// Setup: Use stakes that might cause precision issues
23482348
const minimumStake = await ParametersStorage.minimumStake();
2349-
const maximumStake = await ParametersStorage.maximumStake();
23502349

23512350
const deps = {
23522351
accounts,
@@ -2367,43 +2366,25 @@ describe('@integration RandomSampling', () => {
23672366
await setupNodeWithStakeAndAsk(nodeIdCounter, stake, nodeAsk, deps);
23682367
nodeIdCounter += 2; // Account for both admin and operational accounts
23692368

2370-
const [askLowerBound, askUpperBound] = await AskStorage.getAskBounds();
2371-
23722369
// Calculate actual score from contract
23732370
const actualScore = await RandomSampling.calculateNodeScore(
23742371
publishingNodeIdentityId,
23752372
);
23762373

2377-
// Calculate expected score using the proper contract formula
2378-
const cappedStake = stake > maximumStake ? maximumStake : stake;
2379-
2380-
// Stake factor: 2 * (stake / maxStake)^2
2381-
const stakeRatio = Number(cappedStake) / Number(maximumStake);
2382-
const expectedStakeFactor = 2 * stakeRatio ** 2;
2383-
2384-
// Ask factor: (stakeRatio) * ((upperBound - nodeAsk) / (upperBound - lowerBound))^2
2385-
const nodeAskScaled = Number(nodeAsk) * Number(SCALING_FACTOR);
2386-
let expectedAskFactor = 0;
2387-
if (
2388-
nodeAskScaled >= Number(askLowerBound) &&
2389-
nodeAskScaled <= Number(askUpperBound)
2390-
) {
2391-
const askDiffRatio =
2392-
(Number(askUpperBound) - nodeAskScaled) /
2393-
(Number(askUpperBound) - Number(askLowerBound));
2394-
expectedAskFactor = stakeRatio * askDiffRatio ** 2;
2395-
}
2396-
2397-
// Publishing factor is 0 for this test (no knowledge collections)
2398-
const expectedPublishingFactor = 0;
2399-
const expectedTotal =
2400-
expectedStakeFactor + expectedAskFactor + expectedPublishingFactor;
2401-
2402-
// The precision difference should be very small which indicates that your calculation logic is essentially correct - it's just hitting the limits of floating-point precision in JavaScript.
2403-
expect(Number(actualScore) / Number(SCALING_FACTOR)).to.be.closeTo(
2404-
expectedTotal,
2405-
1e-15,
2374+
// Calculate expected score using our helper that matches the contract logic exactly
2375+
const expectedScore = await calculateExpectedNodeScore(
2376+
BigInt(publishingNodeIdentityId),
2377+
stake,
2378+
{
2379+
ParametersStorage,
2380+
ProfileStorage,
2381+
AskStorage,
2382+
EpochStorage,
2383+
},
24062384
);
2385+
2386+
// The scores should match exactly since we're using the same integer arithmetic
2387+
expect(actualScore).to.equal(expectedScore);
24072388
});
24082389

24092390
it('Should calculate accurate score for node with stake and ask', async () => {

0 commit comments

Comments
 (0)