Skip to content

Commit

Permalink
Process network composition changes
Browse files Browse the repository at this point in the history
  • Loading branch information
DimaStebaev committed Nov 16, 2023
1 parent 6888c9b commit 6f4a8fc
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 10 deletions.
15 changes: 14 additions & 1 deletion contracts/Paymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,18 @@ contract Paymaster is AccessManagedUpgradeable, IPaymaster {

function setNodesAmount(ValidatorId id, uint256 amount) external override restricted {
Validator storage validator = _getValidator(id);
uint256 oldActiveNodesAmount = validator.activeNodesAmount;
validator.nodesAmount = amount;
validator.activeNodesAmount = amount;
_activeNodesAmountChanged(validator, oldActiveNodesAmount, amount);
}

function setActiveNodes(ValidatorId id, uint256 amount) external override restricted {
Validator storage validator = _getValidator(id);
validator.activeNodesAmount = Math.min(amount, validator.nodesAmount);
uint256 oldActiveNodesAmount = validator.activeNodesAmount;
uint256 activeNodesAmount = Math.min(amount, validator.nodesAmount);
validator.activeNodesAmount = activeNodesAmount;
_activeNodesAmountChanged(validator, oldActiveNodesAmount, activeNodesAmount);
}

function setMaxReplenishmentPeriod(Months months) external override restricted {
Expand Down Expand Up @@ -281,6 +286,14 @@ contract Paymaster is AccessManagedUpgradeable, IPaymaster {
}
}

function _activeNodesAmountChanged(Validator storage validator, uint256 oldAmount, uint256 newAmount) private {
Timestamp currentTime = DateTimeUtils.timestamp();
validator.nodesHistory.add(currentTime, newAmount);

uint256 totalNodes = _totalNodesHistory.getLastValue();
_totalNodesHistory.add(currentTime, totalNodes + newAmount - oldAmount);
}

function _getSchain(SchainHash hash) private view returns (Schain storage schain) {
if (_schainHashes.contains(SchainHash.unwrap(hash))) {
return schains[hash];
Expand Down
62 changes: 53 additions & 9 deletions contracts/Sequence.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ pragma solidity ^0.8.19;

// cspell:words deque structs

import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";

import {Timestamp} from "./DateTimeUtils.sol";
import {TypedDoubleEndedQueue} from "./structs/TypedDoubleEndedQueue.sol";


library SequenceLibrary {
type NodeId is uint256;

using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;
using TypedDoubleEndedQueue for TypedDoubleEndedQueue.NodeIdDeque;

error CannotAddToThePast();

uint256 private constant _EMPTY_ITERATOR_INDEX = type(uint256).max;

Expand All @@ -42,7 +43,8 @@ library SequenceLibrary {

struct Sequence {
mapping (NodeId => Node) nodes;
DoubleEndedQueue.Bytes32Deque ids;
TypedDoubleEndedQueue.NodeIdDeque ids;
NodeId freeNodeId;
}

struct Iterator {
Expand All @@ -51,6 +53,21 @@ library SequenceLibrary {
Timestamp nextTimestamp;
}

function add(Sequence storage sequence, Timestamp timestamp, uint256 value) internal {
uint256 length = sequence.ids.length();
if (length > 0) {
if (timestamp <= _getNodeByIndex(sequence, length - 1).timestamp) {
revert CannotAddToThePast();
}
}
NodeId nodeId = _assignId(sequence);
sequence.nodes[nodeId] = Node({
timestamp: timestamp,
value: value
});
sequence.ids.pushBack(nodeId);
}

function getIterator(
Sequence storage sequence,
Timestamp timestamp
Expand Down Expand Up @@ -108,6 +125,15 @@ library SequenceLibrary {
return _getNodeByIndex(sequence, iterator.idIndex).value;
}

function getLastValue(Sequence storage sequence) internal view returns (uint256 lastValue) {
uint256 length = sequence.ids.length();
if (length > 0) {
return _getNodeByIndex(sequence, length - 1).value;
} else {
return 0;
}
}

function step(Iterator memory iterator) internal pure returns (bool success) {
success = hasNext(iterator);
iterator.idIndex += 1;
Expand All @@ -125,19 +151,37 @@ library SequenceLibrary {
delete node.value;
}
sequence.ids.clear();
sequence.freeNodeId = NodeId.wrap(0);
}

// This function is a workaround to allow slither to analyze the code
// because current version fails on
// SequenceLibrary.NodeId.unwrap(value)
// TODO: remove the function after slither fix the issue
function unwrapNodeId(NodeId value) internal pure returns (uint256 unwrappedValue) {
return NodeId.unwrap(value);
}

// This function is a workaround to allow slither to analyze the code
// because current version fails on
// SequenceLibrary.NodeId.wrap(value)
// TODO: remove the function after slither fix the issue
function wrapNodeId(uint256 unwrappedValue) internal pure returns (NodeId wrappedValue) {
return NodeId.wrap(unwrappedValue);
}

// Private

function _assignId(Sequence storage sequence) private returns (NodeId newNodeId) {
newNodeId = sequence.freeNodeId;
sequence.freeNodeId = NodeId.wrap(NodeId.unwrap(newNodeId) + 1);
}

function _getNode(Sequence storage sequence, NodeId nodeId) private view returns (Node storage node) {
return sequence.nodes[nodeId];
}

function _getNodeByIndex(Sequence storage sequence, uint256 index) private view returns (Node storage node) {
return _getNode(sequence, _getNodeId(sequence, index));
}

function _getNodeId(Sequence storage sequence, uint256 index) private view returns (NodeId nodeId) {
nodeId = NodeId.wrap(uint256(sequence.ids.at(index)));
return _getNode(sequence, sequence.ids.at(index));
}
}
83 changes: 83 additions & 0 deletions contracts/structs/TypedDoubleEndedQueue.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: AGPL-3.0-only

/*
TypedDoubleEndedQueue.sol - Paymaster
Copyright (C) 2023-Present SKALE Labs
@author Dmytro Stebaiev
Paymaster 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.
Paymaster 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 Paymaster. If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity ^0.8.20;

// cspell:words deque structs

import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";

import {SequenceLibrary} from "../Sequence.sol";


library TypedDoubleEndedQueue {

struct NodeIdDeque {
DoubleEndedQueue.Bytes32Deque inner;
}

/**
* @dev Inserts an item at the end of the queue.
*
* Reverts with {QueueFull} if the queue is full.
*/
function pushBack(NodeIdDeque storage deque, SequenceLibrary.NodeId value) internal {
DoubleEndedQueue.pushBack(deque.inner, bytes32(SequenceLibrary.unwrapNodeId(value)));
}

/**
* @dev Resets the queue back to being empty.
*
* NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
* out on potential gas refunds.
*/
function clear(NodeIdDeque storage deque) internal {
DoubleEndedQueue.clear(deque.inner);
}

/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with `QueueOutOfBounds` if the index is out of bounds.
*/
function at(NodeIdDeque storage deque, uint256 index) internal view returns (SequenceLibrary.NodeId value) {
return SequenceLibrary.wrapNodeId(
uint256(
DoubleEndedQueue.at(deque.inner, index)
)
);
}

/**
* @dev Returns the number of items in the queue.
*/
function length(NodeIdDeque storage deque) internal view returns (uint256 lengthValue) {
return DoubleEndedQueue.length(deque.inner);
}

/**
* @dev Returns true if the queue is empty.
*/
function empty(NodeIdDeque storage deque) internal view returns (bool isEmpty) {
return DoubleEndedQueue.empty(deque.inner);
}
}

0 comments on commit 6f4a8fc

Please sign in to comment.