diff --git a/contracts/delegation/DelegationController.sol b/contracts/delegation/DelegationController.sol
index 498d190e0..20e54778c 100644
--- a/contracts/delegation/DelegationController.sol
+++ b/contracts/delegation/DelegationController.sol
@@ -26,16 +26,21 @@ import "@openzeppelin/contracts/math/SafeMath.sol";
import "../Permissions.sol";
import "../SkaleToken.sol";
import "../utils/MathUtils.sol";
+import "../utils/FractionUtils.sol";
import "./DelegationPeriodManager.sol";
import "./Punisher.sol";
import "./TokenLaunchLocker.sol";
import "./TokenState.sol";
import "./ValidatorService.sol";
+import "./PartialDifferences.sol";
contract DelegationController is Permissions, ILocker {
using MathUtils for uint;
+ using PartialDifferences for PartialDifferences.Sequence;
+ using PartialDifferences for PartialDifferences.Value;
+ using FractionUtils for FractionUtils.Fraction;
enum State {
PROPOSED,
@@ -58,36 +63,8 @@ contract DelegationController is Permissions, ILocker {
string info;
}
- struct PartialDifferences {
- // month => diff
- mapping (uint => uint) addDiff;
- // month => diff
- mapping (uint => uint) subtractDiff;
- // month => value
- mapping (uint => uint) value;
-
- uint firstUnprocessedMonth;
- uint lastChangedMonth;
- }
-
- struct PartialDifferencesValue {
- // month => diff
- mapping (uint => uint) addDiff;
- // month => diff
- mapping (uint => uint) subtractDiff;
-
- uint value;
- uint firstUnprocessedMonth;
- uint lastChangedMonth;
- }
-
- struct Fraction {
- uint numerator;
- uint denominator;
- }
-
struct SlashingLogEvent {
- Fraction reducingCoefficient;
+ FractionUtils.Fraction reducingCoefficient;
uint nextMonth;
}
@@ -103,7 +80,7 @@ contract DelegationController is Permissions, ILocker {
}
struct SlashingEvent {
- Fraction reducingCoefficient;
+ FractionUtils.Fraction reducingCoefficient;
uint validatorId;
uint month;
}
@@ -154,19 +131,19 @@ contract DelegationController is Permissions, ILocker {
mapping(uint => DelegationExtras) private _delegationExtras;
// validatorId => sequence
- mapping (uint => PartialDifferencesValue) private _delegatedToValidator;
+ mapping (uint => PartialDifferences.Value) private _delegatedToValidator;
// validatorId => sequence
- mapping (uint => PartialDifferences) private _effectiveDelegatedToValidator;
+ mapping (uint => PartialDifferences.Sequence) private _effectiveDelegatedToValidator;
// validatorId => slashing log
mapping (uint => SlashingLog) private _slashesOfValidator;
// holder => sequence
- mapping (address => PartialDifferencesValue) private _delegatedByHolder;
+ mapping (address => PartialDifferences.Value) private _delegatedByHolder;
// holder => validatorId => sequence
- mapping (address => mapping (uint => PartialDifferencesValue)) private _delegatedByHolderToValidator;
+ mapping (address => mapping (uint => PartialDifferences.Value)) private _delegatedByHolderToValidator;
// holder => validatorId => sequence
- mapping (address => mapping (uint => PartialDifferences)) private _effectiveDelegatedByHolderToValidator;
+ mapping (address => mapping (uint => PartialDifferences.Sequence)) private _effectiveDelegatedByHolderToValidator;
SlashingEvent[] private _slashes;
// holder => index in _slashes;
@@ -199,7 +176,7 @@ contract DelegationController is Permissions, ILocker {
allow("Distributor") returns (uint effectiveDelegated)
{
SlashingSignal[] memory slashingSignals = processSlashesWithoutSignals(holder);
- effectiveDelegated = getAndUpdateValue(_effectiveDelegatedByHolderToValidator[holder][validatorId], month);
+ effectiveDelegated = _effectiveDelegatedByHolderToValidator[holder][validatorId].getAndUpdateValue(month);
sendSlashingSignals(slashingSignals);
}
@@ -281,7 +258,9 @@ contract DelegationController is Permissions, ILocker {
uint currentMonth = timeHelpers.getCurrentMonth();
delegations[delegationId].started = currentMonth.add(1);
- _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation = _slashesOfValidator[delegations[delegationId].validatorId].lastMonth;
+ if (_slashesOfValidator[delegations[delegationId].validatorId].lastMonth > 0) {
+ _delegationExtras[delegationId].lastSlashingMonthBeforeDelegation = _slashesOfValidator[delegations[delegationId].validatorId].lastMonth;
+ }
addToDelegatedToValidator(
delegations[delegationId].validatorId,
@@ -375,8 +354,8 @@ contract DelegationController is Permissions, ILocker {
function confiscate(uint validatorId, uint amount) external allow("Punisher") {
uint currentMonth = getCurrentMonth();
- Fraction memory coefficient = reduce(_delegatedToValidator[validatorId], amount, currentMonth);
- reduce(_effectiveDelegatedToValidator[validatorId], coefficient, currentMonth);
+ FractionUtils.Fraction memory coefficient = _delegatedToValidator[validatorId].reduce(amount, currentMonth);
+ _effectiveDelegatedToValidator[validatorId].reduce(coefficient, currentMonth);
putToSlashingLog(_slashesOfValidator[validatorId], coefficient, currentMonth);
_slashes.push(SlashingEvent({reducingCoefficient: coefficient, validatorId: validatorId, month: currentMonth}));
}
@@ -388,7 +367,7 @@ contract DelegationController is Permissions, ILocker {
function getAndUpdateEffectiveDelegatedToValidator(uint validatorId, uint month)
external allow("Distributor") returns (uint)
{
- return getAndUpdateValue(_effectiveDelegatedToValidator[validatorId], month);
+ return _effectiveDelegatedToValidator[validatorId].getAndUpdateValue(month);
}
function getDelegationsByValidatorLength(uint validatorId) external view returns (uint) {
@@ -404,7 +383,7 @@ contract DelegationController is Permissions, ILocker {
}
function getAndUpdateDelegatedToValidator(uint validatorId, uint month) public allow("ValidatorService") returns (uint) {
- return getAndUpdateValue(_delegatedToValidator[validatorId], month);
+ return _delegatedToValidator[validatorId].getAndUpdateValue(month);
}
function getState(uint delegationId) public view checkDelegationExists(delegationId) returns (State state) {
@@ -511,31 +490,31 @@ contract DelegationController is Permissions, ILocker {
}
function addToDelegatedToValidator(uint validatorId, uint amount, uint month) internal {
- add(_delegatedToValidator[validatorId], amount, month);
+ _delegatedToValidator[validatorId].add(amount, month);
}
function addToEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) internal {
- add(_effectiveDelegatedToValidator[validatorId], effectiveAmount, month);
+ _effectiveDelegatedToValidator[validatorId].add(effectiveAmount, month);
}
function addToDelegatedByHolder(address holder, uint amount, uint month) internal {
- add(_delegatedByHolder[holder], amount, month);
+ _delegatedByHolder[holder].add(amount, month);
}
function addToDelegatedByHolderToValidator(
address holder, uint validatorId, uint amount, uint month) internal
{
- add(_delegatedByHolderToValidator[holder][validatorId], amount, month);
+ _delegatedByHolderToValidator[holder][validatorId].add(amount, month);
}
function removeFromDelegatedByHolder(address holder, uint amount, uint month) internal {
- subtract(_delegatedByHolder[holder], amount, month);
+ _delegatedByHolder[holder].subtract(amount, month);
}
function removeFromDelegatedByHolderToValidator(
address holder, uint validatorId, uint amount, uint month) internal
{
- subtract(_delegatedByHolderToValidator[holder][validatorId], amount, month);
+ _delegatedByHolderToValidator[holder][validatorId].subtract(amount, month);
}
function addToEffectiveDelegatedByHolderToValidator(
@@ -545,7 +524,7 @@ contract DelegationController is Permissions, ILocker {
uint month)
internal
{
- add(_effectiveDelegatedByHolderToValidator[holder][validatorId], effectiveAmount, month);
+ _effectiveDelegatedByHolderToValidator[holder][validatorId].add(effectiveAmount, month);
}
function removeFromEffectiveDelegatedByHolderToValidator(
@@ -555,17 +534,17 @@ contract DelegationController is Permissions, ILocker {
uint month)
internal
{
- subtract(_effectiveDelegatedByHolderToValidator[holder][validatorId], effectiveAmount, month);
+ _effectiveDelegatedByHolderToValidator[holder][validatorId].subtract(effectiveAmount, month);
}
function getAndUpdateDelegatedByHolder(address holder) internal returns (uint) {
uint currentMonth = getCurrentMonth();
processAllSlashes(holder);
- return getAndUpdateValue(_delegatedByHolder[holder], currentMonth);
+ return _delegatedByHolder[holder].getAndUpdateValue(currentMonth);
}
function getAndUpdateDelegatedByHolderToValidator(address holder, uint validatorId, uint month) internal returns (uint) {
- return getAndUpdateValue(_delegatedByHolderToValidator[holder][validatorId], month);
+ return _delegatedByHolderToValidator[holder][validatorId].getAndUpdateValue(month);
}
function addToLockedInPendingDelegations(address holder, uint amount) internal returns (uint) {
@@ -618,15 +597,11 @@ contract DelegationController is Permissions, ILocker {
}
function removeFromDelegatedToValidator(uint validatorId, uint amount, uint month) internal {
- subtract(_delegatedToValidator[validatorId], amount, month);
+ _delegatedToValidator[validatorId].subtract(amount, month);
}
function removeFromEffectiveDelegatedToValidator(uint validatorId, uint effectiveAmount, uint month) internal {
- subtract(_effectiveDelegatedToValidator[validatorId], effectiveAmount, month);
- }
-
- function init(PartialDifferences storage sequence) internal {
- sequence.firstUnprocessedMonth = 0;
+ _effectiveDelegatedToValidator[validatorId].subtract(effectiveAmount, month);
}
function calculateDelegationAmountAfterSlashing(uint delegationId) internal view returns (uint) {
@@ -647,232 +622,7 @@ contract DelegationController is Permissions, ILocker {
return amount;
}
- function add(PartialDifferences storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- }
- sequence.addDiff[month] = sequence.addDiff[month].add(diff);
- sequence.lastChangedMonth = month;
- }
-
- function subtract(PartialDifferences storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month, "Cannot subtract from the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- }
- sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
- sequence.lastChangedMonth = month;
- }
-
- function getAndUpdateValue(PartialDifferences storage sequence, uint month) internal returns (uint) {
- if (sequence.firstUnprocessedMonth == 0) {
- return 0;
- }
-
- if (sequence.firstUnprocessedMonth <= month) {
- for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
- sequence.value[i] = sequence.value[i - 1].add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
- delete sequence.addDiff[i];
- delete sequence.subtractDiff[i];
- }
- sequence.firstUnprocessedMonth = month.add(1);
- }
-
- return sequence.value[month];
- }
-
- function add(PartialDifferencesValue storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- sequence.lastChangedMonth = month;
- }
- if (month > sequence.lastChangedMonth) {
- sequence.lastChangedMonth = month;
- }
-
- if (month >= sequence.firstUnprocessedMonth) {
- sequence.addDiff[month] = sequence.addDiff[month].add(diff);
- } else {
- sequence.value = sequence.value.add(diff);
- }
- }
-
- function subtract(PartialDifferencesValue storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month.add(1), "Cannot subtract from the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- sequence.lastChangedMonth = month;
- }
- if (month > sequence.lastChangedMonth) {
- sequence.lastChangedMonth = month;
- }
-
- if (month >= sequence.firstUnprocessedMonth) {
- sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
- } else {
- sequence.value = sequence.value.boundedSub(diff);
- }
- }
-
- function getAndUpdateValue(PartialDifferencesValue storage sequence, uint month) internal returns (uint) {
- require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot calculate value in the past");
- if (sequence.firstUnprocessedMonth == 0) {
- return 0;
- }
-
- if (sequence.firstUnprocessedMonth <= month) {
- for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
- sequence.value = sequence.value.add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
- delete sequence.addDiff[i];
- delete sequence.subtractDiff[i];
- }
- sequence.firstUnprocessedMonth = month.add(1);
- }
-
- return sequence.value;
- }
-
- function reduce(PartialDifferencesValue storage sequence, uint amount, uint month) internal returns (Fraction memory) {
- require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
- if (sequence.firstUnprocessedMonth == 0) {
- return createFraction(0);
- }
- uint value = getAndUpdateValue(sequence, month);
- if (value.approximatelyEqual(0)) {
- return createFraction(0);
- }
-
- uint _amount = amount;
- if (value < amount) {
- _amount = value;
- }
-
- Fraction memory reducingCoefficient = createFraction(value.boundedSub(_amount), value);
- reduce(sequence, reducingCoefficient, month);
- return reducingCoefficient;
- }
-
- function reduce(PartialDifferencesValue storage sequence, Fraction memory reducingCoefficient, uint month) internal {
- reduce(
- sequence,
- sequence,
- reducingCoefficient,
- month,
- false);
- }
-
- function reduce(
- PartialDifferencesValue storage sequence,
- PartialDifferencesValue storage sumSequence,
- Fraction memory reducingCoefficient,
- uint month) internal
- {
- reduce(
- sequence,
- sumSequence,
- reducingCoefficient,
- month,
- true);
- }
-
- function reduce(
- PartialDifferencesValue storage sequence,
- PartialDifferencesValue storage sumSequence,
- Fraction memory reducingCoefficient,
- uint month,
- bool hasSumSequence) internal
- {
- require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
- if (hasSumSequence) {
- require(month.add(1) >= sumSequence.firstUnprocessedMonth, "Cannot reduce value in the past");
- }
- require(reducingCoefficient.numerator <= reducingCoefficient.denominator, "Increasing of values is not implemented");
- if (sequence.firstUnprocessedMonth == 0) {
- return;
- }
- uint value = getAndUpdateValue(sequence, month);
- if (value.approximatelyEqual(0)) {
- return;
- }
-
- uint newValue = sequence.value.mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
- if (hasSumSequence) {
- subtract(sumSequence, sequence.value.boundedSub(newValue), month);
- }
- sequence.value = newValue;
-
- for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
- uint newDiff = sequence.subtractDiff[i].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
- if (hasSumSequence) {
- sumSequence.subtractDiff[i] = sumSequence.subtractDiff[i].boundedSub(sequence.subtractDiff[i].boundedSub(newDiff));
- }
- sequence.subtractDiff[i] = newDiff;
- }
- }
-
- function reduce(
- PartialDifferences storage sequence,
- Fraction memory reducingCoefficient,
- uint month) internal
- {
- require(month.add(1) >= sequence.firstUnprocessedMonth, "Can't reduce value in the past");
- require(reducingCoefficient.numerator <= reducingCoefficient.denominator, "Increasing of values is not implemented");
- if (sequence.firstUnprocessedMonth == 0) {
- return;
- }
- uint value = getAndUpdateValue(sequence, month);
- if (value.approximatelyEqual(0)) {
- return;
- }
-
- sequence.value[month] = sequence.value[month].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
-
- for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
- sequence.subtractDiff[i] = sequence.subtractDiff[i].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
- }
- }
-
- function createFraction(uint numerator, uint denominator) internal pure returns (Fraction memory) {
- require(denominator > 0, "Division by zero");
- Fraction memory fraction = Fraction({numerator: numerator, denominator: denominator});
- reduceFraction(fraction);
- return fraction;
- }
-
- function createFraction(uint value) internal pure returns (Fraction memory) {
- return createFraction(value, 1);
- }
-
- function reduceFraction(Fraction memory fraction) internal pure {
- uint _gcd = gcd(fraction.numerator, fraction.denominator);
- fraction.numerator = fraction.numerator.div(_gcd);
- fraction.denominator = fraction.denominator.div(_gcd);
- }
-
- function multiplyFraction(Fraction memory a, Fraction memory b) internal pure returns (Fraction memory) {
- return createFraction(a.numerator.mul(b.numerator), a.denominator.mul(b.denominator));
- }
-
- function gcd(uint _a, uint _b) internal pure returns (uint) {
- uint a = _a;
- uint b = _b;
- if (b > a) {
- (a, b) = swap(a, b);
- }
- while (b > 0) {
- a = a.mod(b);
- (a, b) = swap (a, b);
- }
- return a;
- }
-
- function swap(uint a, uint b) internal pure returns (uint, uint) {
- return (b, a);
- }
-
- function putToSlashingLog(SlashingLog storage log, Fraction memory coefficient, uint month) internal {
+ function putToSlashingLog(SlashingLog storage log, FractionUtils.Fraction memory coefficient, uint month) internal {
if (log.firstMonth == 0) {
log.firstMonth = month;
log.lastMonth = month;
@@ -881,7 +631,7 @@ contract DelegationController is Permissions, ILocker {
} else {
require(log.lastMonth <= month, "Cannot put slashing event in the past");
if (log.lastMonth == month) {
- log.slashes[month].reducingCoefficient = multiplyFraction(log.slashes[month].reducingCoefficient, coefficient);
+ log.slashes[month].reducingCoefficient = log.slashes[month].reducingCoefficient.multiplyFraction(coefficient);
} else {
log.slashes[month].reducingCoefficient = coefficient;
log.slashes[month].nextMonth = 0;
@@ -905,13 +655,11 @@ contract DelegationController is Permissions, ILocker {
uint month = _slashes[index].month;
uint oldValue = getAndUpdateDelegatedByHolderToValidator(holder, validatorId, month);
if (oldValue.muchGreater(0)) {
- reduce(
- _delegatedByHolderToValidator[holder][validatorId],
+ _delegatedByHolderToValidator[holder][validatorId].reduce(
_delegatedByHolder[holder],
_slashes[index].reducingCoefficient,
month);
- reduce(
- _effectiveDelegatedByHolderToValidator[holder][validatorId],
+ _effectiveDelegatedByHolderToValidator[holder][validatorId].reduce(
_slashes[index].reducingCoefficient,
month);
slashingSignals[index.sub(begin)].holder = holder;
diff --git a/contracts/delegation/PartialDifferences.sol b/contracts/delegation/PartialDifferences.sol
new file mode 100644
index 000000000..5bd4339e4
--- /dev/null
+++ b/contracts/delegation/PartialDifferences.sol
@@ -0,0 +1,281 @@
+/*
+ PartialDifferences.sol - SKALE Manager
+ Copyright (C) 2018-Present SKALE Labs
+ @author Dmytro Stebaiev
+
+ SKALE Manager is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SKALE Manager is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with SKALE Manager. If not, see .
+*/
+
+pragma solidity 0.5.16;
+
+import "../utils/MathUtils.sol";
+import "../utils/FractionUtils.sol";
+
+
+library PartialDifferences {
+ using SafeMath for uint;
+ using MathUtils for uint;
+
+ struct Sequence {
+ // month => diff
+ mapping (uint => uint) addDiff;
+ // month => diff
+ mapping (uint => uint) subtractDiff;
+ // month => value
+ mapping (uint => uint) value;
+
+ uint firstUnprocessedMonth;
+ uint lastChangedMonth;
+ }
+
+ struct Value {
+ // month => diff
+ mapping (uint => uint) addDiff;
+ // month => diff
+ mapping (uint => uint) subtractDiff;
+
+ uint value;
+ uint firstUnprocessedMonth;
+ uint lastChangedMonth;
+ }
+
+ // functions for sequence
+
+ function add(Sequence storage sequence, uint diff, uint month) internal {
+ require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ sequence.firstUnprocessedMonth = month;
+ }
+ sequence.addDiff[month] = sequence.addDiff[month].add(diff);
+ if (sequence.lastChangedMonth != month) {
+ sequence.lastChangedMonth = month;
+ }
+ }
+
+ function subtract(Sequence storage sequence, uint diff, uint month) internal {
+ require(sequence.firstUnprocessedMonth <= month, "Cannot subtract from the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ sequence.firstUnprocessedMonth = month;
+ }
+ sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
+ if (sequence.lastChangedMonth != month) {
+ sequence.lastChangedMonth = month;
+ }
+ }
+
+ function getAndUpdateValue(Sequence storage sequence, uint month) internal returns (uint) {
+ if (sequence.firstUnprocessedMonth == 0) {
+ return 0;
+ }
+
+ if (sequence.firstUnprocessedMonth <= month) {
+ for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
+ uint nextValue = sequence.value[i - 1].add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
+ if (sequence.value[i] != nextValue) {
+ sequence.value[i] = nextValue;
+ }
+ if (sequence.addDiff[i] > 0) {
+ delete sequence.addDiff[i];
+ }
+ if (sequence.subtractDiff[i] > 0) {
+ delete sequence.subtractDiff[i];
+ }
+ }
+ sequence.firstUnprocessedMonth = month.add(1);
+ }
+
+ return sequence.value[month];
+ }
+
+ function reduce(
+ Sequence storage sequence,
+ FractionUtils.Fraction memory reducingCoefficient,
+ uint month) internal
+ {
+ require(month.add(1) >= sequence.firstUnprocessedMonth, "Can't reduce value in the past");
+ require(reducingCoefficient.numerator <= reducingCoefficient.denominator, "Increasing of values is not implemented");
+ if (sequence.firstUnprocessedMonth == 0) {
+ return;
+ }
+ uint value = getAndUpdateValue(sequence, month);
+ if (value.approximatelyEqual(0)) {
+ return;
+ }
+
+ sequence.value[month] = sequence.value[month].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
+
+ for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
+ sequence.subtractDiff[i] = sequence.subtractDiff[i].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
+ }
+ }
+
+ // functions for value
+
+ function add(Value storage sequence, uint diff, uint month) internal {
+ require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ sequence.firstUnprocessedMonth = month;
+ sequence.lastChangedMonth = month;
+ }
+ if (month > sequence.lastChangedMonth) {
+ sequence.lastChangedMonth = month;
+ }
+
+ if (month >= sequence.firstUnprocessedMonth) {
+ sequence.addDiff[month] = sequence.addDiff[month].add(diff);
+ } else {
+ sequence.value = sequence.value.add(diff);
+ }
+ }
+
+ function subtract(Value storage sequence, uint diff, uint month) internal {
+ require(sequence.firstUnprocessedMonth <= month.add(1), "Cannot subtract from the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ sequence.firstUnprocessedMonth = month;
+ sequence.lastChangedMonth = month;
+ }
+ if (month > sequence.lastChangedMonth) {
+ sequence.lastChangedMonth = month;
+ }
+
+ if (month >= sequence.firstUnprocessedMonth) {
+ sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
+ } else {
+ sequence.value = sequence.value.boundedSub(diff);
+ }
+ }
+
+ function getAndUpdateValue(Value storage sequence, uint month) internal returns (uint) {
+ require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot calculate value in the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ return 0;
+ }
+
+ if (sequence.firstUnprocessedMonth <= month) {
+ for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
+ uint newValue = sequence.value.add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
+ if (sequence.value != newValue) {
+ sequence.value = newValue;
+ }
+ if (sequence.addDiff[i] > 0) {
+ delete sequence.addDiff[i];
+ }
+ if (sequence.subtractDiff[i] > 0) {
+ delete sequence.subtractDiff[i];
+ }
+ }
+ sequence.firstUnprocessedMonth = month.add(1);
+ }
+
+ return sequence.value;
+ }
+
+ function reduce(Value storage sequence, uint amount, uint month) internal returns (FractionUtils.Fraction memory) {
+ require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
+ if (sequence.firstUnprocessedMonth == 0) {
+ return FractionUtils.createFraction(0);
+ }
+ uint value = getAndUpdateValue(sequence, month);
+ if (value.approximatelyEqual(0)) {
+ return FractionUtils.createFraction(0);
+ }
+
+ uint _amount = amount;
+ if (value < amount) {
+ _amount = value;
+ }
+
+ FractionUtils.Fraction memory reducingCoefficient = FractionUtils.createFraction(value.boundedSub(_amount), value);
+ reduce(sequence, reducingCoefficient, month);
+ return reducingCoefficient;
+ }
+
+ function reduce(Value storage sequence, FractionUtils.Fraction memory reducingCoefficient, uint month) internal {
+ reduce(
+ sequence,
+ sequence,
+ reducingCoefficient,
+ month,
+ false);
+ }
+
+ function reduce(
+ Value storage sequence,
+ Value storage sumSequence,
+ FractionUtils.Fraction memory reducingCoefficient,
+ uint month) internal
+ {
+ reduce(
+ sequence,
+ sumSequence,
+ reducingCoefficient,
+ month,
+ true);
+ }
+
+ function reduce(
+ Value storage sequence,
+ Value storage sumSequence,
+ FractionUtils.Fraction memory reducingCoefficient,
+ uint month,
+ bool hasSumSequence) internal
+ {
+ require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot reduce value in the past");
+ if (hasSumSequence) {
+ require(month.add(1) >= sumSequence.firstUnprocessedMonth, "Cannot reduce value in the past");
+ }
+ require(reducingCoefficient.numerator <= reducingCoefficient.denominator, "Increasing of values is not implemented");
+ if (sequence.firstUnprocessedMonth == 0) {
+ return;
+ }
+ uint value = getAndUpdateValue(sequence, month);
+ if (value.approximatelyEqual(0)) {
+ return;
+ }
+
+ uint newValue = sequence.value.mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
+ if (hasSumSequence) {
+ subtract(sumSequence, sequence.value.boundedSub(newValue), month);
+ }
+ sequence.value = newValue;
+
+ for (uint i = month.add(1); i <= sequence.lastChangedMonth; ++i) {
+ uint newDiff = sequence.subtractDiff[i].mul(reducingCoefficient.numerator).div(reducingCoefficient.denominator);
+ if (hasSumSequence) {
+ sumSequence.subtractDiff[i] = sumSequence.subtractDiff[i].boundedSub(sequence.subtractDiff[i].boundedSub(newDiff));
+ }
+ sequence.subtractDiff[i] = newDiff;
+ }
+ }
+
+ function clear(Value storage sequence) internal {
+ for (uint i = sequence.firstUnprocessedMonth; i <= sequence.lastChangedMonth; ++i) {
+ if (sequence.addDiff[i] > 0) {
+ delete sequence.addDiff[i];
+ }
+ if (sequence.subtractDiff[i] > 0) {
+ delete sequence.subtractDiff[i];
+ }
+ }
+ if (sequence.value > 0) {
+ delete sequence.value;
+ }
+ if (sequence.firstUnprocessedMonth > 0) {
+ delete sequence.firstUnprocessedMonth;
+ }
+ if (sequence.lastChangedMonth > 0) {
+ delete sequence.lastChangedMonth;
+ }
+ }
+}
\ No newline at end of file
diff --git a/contracts/delegation/TokenLaunchLocker.sol b/contracts/delegation/TokenLaunchLocker.sol
index f5da72614..d4f1d957e 100644
--- a/contracts/delegation/TokenLaunchLocker.sol
+++ b/contracts/delegation/TokenLaunchLocker.sol
@@ -28,10 +28,12 @@ import "../utils/MathUtils.sol";
import "./DelegationController.sol";
import "./TimeHelpers.sol";
+import "./PartialDifferences.sol";
contract TokenLaunchLocker is Permissions, ILocker {
using MathUtils for uint;
+ using PartialDifferences for PartialDifferences.Value;
event Unlocked(
address holder,
@@ -43,17 +45,6 @@ contract TokenLaunchLocker is Permissions, ILocker {
uint amount
);
- struct PartialDifferencesValue {
- // month => diff
- mapping (uint => uint) addDiff;
- // month => diff
- mapping (uint => uint) subtractDiff;
-
- uint value;
- uint firstUnprocessedMonth;
- uint lastChangedMonth;
- }
-
struct DelegatedAmountAndMonth {
uint delegated;
uint month;
@@ -63,7 +54,7 @@ contract TokenLaunchLocker is Permissions, ILocker {
mapping (address => uint) private _locked;
// holder => tokens
- mapping (address => PartialDifferencesValue) private _delegatedAmount;
+ mapping (address => PartialDifferences.Value) private _delegatedAmount;
mapping (address => DelegatedAmountAndMonth) private _totalDelegatedAmount;
@@ -142,15 +133,15 @@ contract TokenLaunchLocker is Permissions, ILocker {
// private
function getAndUpdateDelegatedAmount(address holder, uint currentMonth) internal returns (uint) {
- return getAndUpdateValue(_delegatedAmount[holder], currentMonth);
+ return _delegatedAmount[holder].getAndUpdateValue(currentMonth);
}
function addToDelegatedAmount(address holder, uint amount, uint month) internal {
- add(_delegatedAmount[holder], amount, month);
+ _delegatedAmount[holder].add(amount, month);
}
function removeFromDelegatedAmount(address holder, uint amount, uint month) internal {
- subtract(_delegatedAmount[holder], amount, month);
+ _delegatedAmount[holder].subtract(amount, month);
}
function addToTotalDelegatedAmount(address holder, uint amount, uint month) internal {
@@ -174,73 +165,11 @@ contract TokenLaunchLocker is Permissions, ILocker {
}
function deleteDelegatedAmount(address holder) internal {
- deletePartialDifferencesValue(_delegatedAmount[holder]);
+ _delegatedAmount[holder].clear();
}
function deleteTotalDelegatedAmount(address holder) internal {
delete _totalDelegatedAmount[holder].delegated;
delete _totalDelegatedAmount[holder].month;
}
-
- function add(PartialDifferencesValue storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month, "Cannot add to the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- sequence.lastChangedMonth = month;
- }
- if (month > sequence.lastChangedMonth) {
- sequence.lastChangedMonth = month;
- }
-
- if (month >= sequence.firstUnprocessedMonth) {
- sequence.addDiff[month] = sequence.addDiff[month].add(diff);
- } else {
- sequence.value = sequence.value.add(diff);
- }
- }
-
- function subtract(PartialDifferencesValue storage sequence, uint diff, uint month) internal {
- require(sequence.firstUnprocessedMonth <= month.add(1), "Cannot subtract from the past");
- if (sequence.firstUnprocessedMonth == 0) {
- sequence.firstUnprocessedMonth = month;
- sequence.lastChangedMonth = month;
- }
- if (month > sequence.lastChangedMonth) {
- sequence.lastChangedMonth = month;
- }
-
- if (month >= sequence.firstUnprocessedMonth) {
- sequence.subtractDiff[month] = sequence.subtractDiff[month].add(diff);
- } else {
- sequence.value = sequence.value.boundedSub(diff);
- }
- }
-
- function getAndUpdateValue(PartialDifferencesValue storage sequence, uint month) internal returns (uint) {
- require(month.add(1) >= sequence.firstUnprocessedMonth, "Cannot calculate value in the past");
- if (sequence.firstUnprocessedMonth == 0) {
- return 0;
- }
-
- if (sequence.firstUnprocessedMonth <= month) {
- for (uint i = sequence.firstUnprocessedMonth; i <= month; ++i) {
- sequence.value = sequence.value.add(sequence.addDiff[i]).boundedSub(sequence.subtractDiff[i]);
- delete sequence.addDiff[i];
- delete sequence.subtractDiff[i];
- }
- sequence.firstUnprocessedMonth = month.add(1);
- }
-
- return sequence.value;
- }
-
- function deletePartialDifferencesValue(PartialDifferencesValue storage sequence) internal {
- for (uint i = sequence.firstUnprocessedMonth; i <= sequence.lastChangedMonth; ++i) {
- delete sequence.addDiff[i];
- delete sequence.subtractDiff[i];
- }
- delete sequence.value;
- delete sequence.firstUnprocessedMonth;
- delete sequence.lastChangedMonth;
- }
}
\ No newline at end of file
diff --git a/contracts/delegation/TokenLaunchManager.sol b/contracts/delegation/TokenLaunchManager.sol
index 1ed49c247..875ca1d70 100644
--- a/contracts/delegation/TokenLaunchManager.sol
+++ b/contracts/delegation/TokenLaunchManager.sol
@@ -64,7 +64,7 @@ contract TokenLaunchManager is Permissions, IERC777Recipient {
function retrieve() external {
require(approved[_msgSender()] > 0, "Transfer is not approved");
uint value = approved[_msgSender()];
- approved[_msgSender()] = 0;
+ delete approved[_msgSender()];
require(IERC20(contractManager.getContract("SkaleToken")).transfer(_msgSender(), value), "Error of token sending");
TokenLaunchLocker(contractManager.getContract("TokenLaunchLocker")).lock(_msgSender(), value);
emit TokensRetrieved(_msgSender(), value);
diff --git a/contracts/delegation/ValidatorService.sol b/contracts/delegation/ValidatorService.sol
index 4617bb12b..3467a9536 100644
--- a/contracts/delegation/ValidatorService.sol
+++ b/contracts/delegation/ValidatorService.sol
@@ -115,11 +115,13 @@ contract ValidatorService is Permissions {
}
function enableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyOwner {
+ require(!trustedValidators[validatorId], "Validator is already enabled");
trustedValidators[validatorId] = true;
emit ValidatorWasEnabled(validatorId);
}
function disableValidator(uint validatorId) external checkValidatorExists(validatorId) onlyOwner {
+ require(trustedValidators[validatorId], "Validator is already disabled");
trustedValidators[validatorId] = false;
emit ValidatorWasDisabled(validatorId);
}
@@ -156,7 +158,7 @@ contract ValidatorService is Permissions {
getValidator(validatorId).requestedAddress == msg.sender,
"The validator address cannot be changed because it is not the actual owner"
);
- validators[validatorId].requestedAddress = address(0);
+ delete validators[validatorId].requestedAddress;
setValidatorAddress(validatorId, msg.sender);
emit ValidatorAddressChanged(validatorId, validators[validatorId].validatorAddress);
diff --git a/contracts/test/PartialDifferencesTester.sol b/contracts/test/PartialDifferencesTester.sol
new file mode 100644
index 000000000..b85e03465
--- /dev/null
+++ b/contracts/test/PartialDifferencesTester.sol
@@ -0,0 +1,67 @@
+/*
+ PartialDifferencesTester.sol - SKALE Manager
+ Copyright (C) 2018-Present SKALE Labs
+ @author Dmytro Stebaiev
+
+ SKALE Manager is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SKALE Manager is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with SKALE Manager. If not, see .
+*/
+
+pragma solidity 0.5.16;
+
+import "../delegation/PartialDifferences.sol";
+
+
+contract PartialDifferencesTester {
+ using PartialDifferences for PartialDifferences.Sequence;
+ using PartialDifferences for PartialDifferences.Value;
+
+ PartialDifferences.Sequence[] sequences;
+ PartialDifferences.Value[] values;
+
+ function createSequence() external returns (uint id) {
+ id = sequences.length;
+ ++sequences.length;
+ }
+
+ function latestSequence() external view returns (uint id) {
+ require(sequences.length > 0, "There are no sequences");
+ return sequences.length - 1;
+ }
+
+ function addToSequence(uint sequence, uint diff, uint month) external {
+ require(sequence < sequences.length, "Sequence does not exist");
+ sequences[sequence].add(diff, month);
+ }
+
+ function subtractFromSequence(uint sequence, uint diff, uint month) external {
+ require(sequence < sequences.length, "Sequence does not exist");
+ sequences[sequence].subtract(diff, month);
+ }
+
+ function getAndUpdateSequenceItem(uint sequence, uint month) external returns (uint) {
+ require(sequence < sequences.length, "Sequence does not exist");
+ return sequences[sequence].getAndUpdateValue(month);
+ }
+
+ function reduceSequence(
+ uint sequence,
+ uint a,
+ uint b,
+ uint month) external
+ {
+ require(sequence < sequences.length, "Sequence does not exist");
+ FractionUtils.Fraction memory reducingCoefficient = FractionUtils.createFraction(a, b);
+ return sequences[sequence].reduce(reducingCoefficient, month);
+ }
+}
\ No newline at end of file
diff --git a/contracts/utils/FractionUtils.sol b/contracts/utils/FractionUtils.sol
new file mode 100644
index 000000000..91bc77110
--- /dev/null
+++ b/contracts/utils/FractionUtils.sol
@@ -0,0 +1,70 @@
+/*
+ FractionUtils.sol - SKALE Manager
+ Copyright (C) 2018-Present SKALE Labs
+ @author Dmytro Stebaiev
+
+ SKALE Manager is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ SKALE Manager is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with SKALE Manager. If not, see .
+*/
+
+pragma solidity 0.5.16;
+
+import "@openzeppelin/contracts/math/SafeMath.sol";
+
+
+library FractionUtils {
+ using SafeMath for uint;
+
+ struct Fraction {
+ uint numerator;
+ uint denominator;
+ }
+
+ function createFraction(uint numerator, uint denominator) internal pure returns (Fraction memory) {
+ require(denominator > 0, "Division by zero");
+ Fraction memory fraction = Fraction({numerator: numerator, denominator: denominator});
+ reduceFraction(fraction);
+ return fraction;
+ }
+
+ function createFraction(uint value) internal pure returns (Fraction memory) {
+ return createFraction(value, 1);
+ }
+
+ function reduceFraction(Fraction memory fraction) internal pure {
+ uint _gcd = gcd(fraction.numerator, fraction.denominator);
+ fraction.numerator = fraction.numerator.div(_gcd);
+ fraction.denominator = fraction.denominator.div(_gcd);
+ }
+
+ function multiplyFraction(Fraction memory a, Fraction memory b) internal pure returns (Fraction memory) {
+ return createFraction(a.numerator.mul(b.numerator), a.denominator.mul(b.denominator));
+ }
+
+ function gcd(uint _a, uint _b) internal pure returns (uint) {
+ uint a = _a;
+ uint b = _b;
+ if (b > a) {
+ (a, b) = swap(a, b);
+ }
+ while (b > 0) {
+ a = a.mod(b);
+ (a, b) = swap (a, b);
+ }
+ return a;
+ }
+
+ function swap(uint a, uint b) internal pure returns (uint, uint) {
+ return (b, a);
+ }
+}
\ No newline at end of file
diff --git a/test/delegation/PartialDifferences.ts b/test/delegation/PartialDifferences.ts
new file mode 100644
index 000000000..f90dd9805
--- /dev/null
+++ b/test/delegation/PartialDifferences.ts
@@ -0,0 +1,55 @@
+import { deployContractManager } from "../tools/deploy/contractManager";
+import { deployPartialDifferencesTester } from "../tools/deploy/test/partialDifferencesTester";
+import { PartialDifferencesTesterInstance } from "../../types/truffle-contracts";
+import * as chai from "chai";
+import * as chaiAsPromised from "chai-as-promised";
+
+chai.should();
+chai.use(chaiAsPromised);
+
+contract("PartialDifferences", ([owner]) => {
+ let contractManager;
+ let partialDifferencesTester: PartialDifferencesTesterInstance;
+ before(async () => {
+ contractManager = await deployContractManager();
+ partialDifferencesTester = await deployPartialDifferencesTester(contractManager);
+ })
+
+ it("should calculate sequences correctly", async () => {
+ await partialDifferencesTester.createSequence();
+ let sequence = await partialDifferencesTester.latestSequence();
+
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 1)).toNumber().should.be.equal(0);
+ await partialDifferencesTester.reduceSequence(sequence, 1, 2, 2);
+
+ await partialDifferencesTester.addToSequence(sequence, 5e7, 1);
+ await partialDifferencesTester.subtractFromSequence(sequence, 3e7, 3);
+ await partialDifferencesTester.addToSequence(sequence, 1e7, 4);
+ await partialDifferencesTester.subtractFromSequence(sequence, 5e7, 5);
+ await partialDifferencesTester.addToSequence(sequence, 1e7, 4);
+ await partialDifferencesTester.addToSequence(sequence, 1e7, 4);
+
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 1)).toNumber().should.be.equal(5e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 2)).toNumber().should.be.equal(5e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 3)).toNumber().should.be.equal(2e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 4)).toNumber().should.be.equal(5e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 5)).toNumber().should.be.equal(0);
+
+ await partialDifferencesTester.reduceSequence(sequence, 1, 2, 2);
+
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 1)).toNumber().should.be.equal(5e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 2)).toNumber().should.be.equal(25e6);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 3)).toNumber().should.be.equal(1e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 4)).toNumber().should.be.equal(4e7);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 5)).toNumber().should.be.equal(0);
+
+ await partialDifferencesTester.createSequence();
+ sequence = await partialDifferencesTester.latestSequence();
+ await partialDifferencesTester.subtractFromSequence(sequence, 1, 1);
+ await partialDifferencesTester.addToSequence(sequence, 1, 1);
+ await partialDifferencesTester.getAndUpdateSequenceItem(sequence, 1);
+ await partialDifferencesTester.reduceSequence(sequence, 1, 2, 1);
+ (await partialDifferencesTester.getAndUpdateSequenceItem.call(sequence, 1)).toNumber().should.be.equal(0);
+
+ });
+});
\ No newline at end of file
diff --git a/test/delegation/ValidatorService.ts b/test/delegation/ValidatorService.ts
index 1b51901bd..1470b6329 100644
--- a/test/delegation/ValidatorService.ts
+++ b/test/delegation/ValidatorService.ts
@@ -295,7 +295,13 @@ contract("ValidatorService", ([owner, holder, validator1, validator2, validator3
it("should allow to disable validator from whitelist", async () => {
await validatorService.disableValidator(validatorId, {from: validator1})
.should.be.eventually.rejectedWith("Ownable: caller is not the owner");
+ await validatorService.disableValidator(validatorId, {from: owner})
+ .should.be.eventually.rejectedWith("Validator is already disabled");
+
+ await validatorService.enableValidator(validatorId, {from: owner});
+ await validatorService.trustedValidators(validatorId).should.eventually.be.true;
await validatorService.disableValidator(validatorId, {from: owner});
+ await validatorService.trustedValidators(validatorId).should.eventually.be.false;
});
it("should not allow to send delegation request if validator isn't authorized", async () => {
diff --git a/test/tools/deploy/test/partialDifferencesTester.ts b/test/tools/deploy/test/partialDifferencesTester.ts
new file mode 100644
index 000000000..e5ec6fd58
--- /dev/null
+++ b/test/tools/deploy/test/partialDifferencesTester.ts
@@ -0,0 +1,10 @@
+import { ContractManagerInstance, PartialDifferencesTesterInstance } from "../../../../types/truffle-contracts";
+import { deployWithConstructorFunctionFactory } from "../factory";
+
+const deployPartialDifferencesTester: (contractManager: ContractManagerInstance) => Promise
+ = deployWithConstructorFunctionFactory("PartialDifferencesTester",
+ async (contractManager: ContractManagerInstance) => {
+ return undefined;
+ });
+
+export { deployPartialDifferencesTester };