From 6607a4259912753435591b8f4171e0834707f793 Mon Sep 17 00:00:00 2001 From: Alexandre Tolstenko Date: Sat, 9 Apr 2022 17:58:26 -0300 Subject: [PATCH] feat: Max Heap fixed and updated to newest solidity version --- .gitignore | 2 + contracts/CircuitBreaker.sol | 28 ++++- contracts/Heap.sol | 230 ++++++++++++++++++----------------- 3 files changed, 143 insertions(+), 117 deletions(-) diff --git a/.gitignore b/.gitignore index ad529e7..77f5d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ artifacts .idea/ yarn.lock + +package-lock.json diff --git a/contracts/CircuitBreaker.sol b/contracts/CircuitBreaker.sol index 9ee2466..f33f65e 100644 --- a/contracts/CircuitBreaker.sol +++ b/contracts/CircuitBreaker.sol @@ -2,11 +2,27 @@ pragma solidity ^0.8.9; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./Heap.sol"; + contract CircuitBreaker { -// struct Bucket { -// uint256 max; -// uint256 min; -// }; -// mapping (uint -> Bucket) buckets; -// uint256 + using Heap for Heap.Data; + Heap.Data public data; + using SafeMath for uint256; + + uint32 bucketTimeLimit = 15 minutes; // 15 min + uint32 constant BUCKET_TIME_LIMIT = 1 hours; // 1h + + uint32 bucketSizeLimit = 96; // 96*15m = 24h + uint32 constant BUCKET_SIZE_LIMIT = 128; + + constructor() public { data.init(); } + + // @dev get Max elem; + // @dev remove buckets older than a datetime. // todo: how to do it? + // @dev + + // todo: how to remove old buckets? + // @dev Used to find the exactly bucket. It is useful to find the exacly bucket by time and remove the older ones + } \ No newline at end of file diff --git a/contracts/Heap.sol b/contracts/Heap.sol index d090292..55380b2 100644 --- a/contracts/Heap.sol +++ b/contracts/Heap.sol @@ -1,111 +1,119 @@ -//pragma solidity ^0.8.9; -// -//// Eth Heap -//// Author: Zac Mitton -//// License: MIT -//// ref.: https://github.com/zmitton/eth-heap/blob/master/contracts/Heap.sol -// -//library Heap{ // default max-heap -// -// uint constant ROOT_INDEX = 1; -// -// struct Data{ -// int128 idCount; -// Node[] nodes; // root is index 1; index 0 not used -// mapping (int128 => uint) indices; // unique id => node index -// } -// struct Node{ -// int128 id; //use with another mapping to store arbitrary object types -// int128 priority; -// } -// -// //call init before anything else -// function init(Data storage self) internal{ -// if(self.nodes.length == 0) self.nodes.push(Node(0,0)); -// } -// -// function insert(Data storage self, int128 priority) internal returns(Node memory){//√ -// if(self.nodes.length == 0){ init(self); }// test on-the-fly-init -// self.idCount++; -// self.nodes.length++; -// Node memory n = Node(self.idCount, priority); -// _bubbleUp(self, n, self.nodes.length-1); -// return n; -// } -// function extractMax(Data storage self) internal returns(Node){//√ -// return _extract(self, ROOT_INDEX); -// } -// function extractById(Data storage self, int128 id) internal returns(Node){//√ -// return _extract(self, self.indices[id]); -// } -// -// //view -// function dump(Data storage self) internal view returns(Node[]){ -// //note: Empty set will return `[Node(0,0)]`. uninitialized will return `[]`. -// return self.nodes; -// } -// function getById(Data storage self, int128 id) internal view returns(Node){ -// return getByIndex(self, self.indices[id]);//test that all these return the emptyNode -// } -// function getByIndex(Data storage self, uint i) internal view returns(Node){ -// return self.nodes.length > i ? self.nodes[i] : Node(0,0); -// } -// function getMax(Data storage self) internal view returns(Node){ -// return getByIndex(self, ROOT_INDEX); -// } -// function size(Data storage self) internal view returns(uint){ -// return self.nodes.length > 0 ? self.nodes.length-1 : 0; -// } -// function isNode(Node n) internal pure returns(bool){ return n.id > 0; } -// -// //private -// function _extract(Data storage self, uint i) private returns(Node){//√ -// if(self.nodes.length <= i || i <= 0){ return Node(0,0); } -// -// Node memory extractedNode = self.nodes[i]; -// delete self.indices[extractedNode.id]; -// -// Node memory tailNode = self.nodes[self.nodes.length-1]; -// self.nodes.length--; -// -// if(i < self.nodes.length){ // if extracted node was not tail -// _bubbleUp(self, tailNode, i); -// _bubbleDown(self, self.nodes[i], i); // then try bubbling down -// } -// return extractedNode; -// } -// function _bubbleUp(Data storage self, Node memory n, uint i) private{//√ -// if(i==ROOT_INDEX || n.priority <= self.nodes[i/2].priority){ -// _insert(self, n, i); -// }else{ -// _insert(self, self.nodes[i/2], i); -// _bubbleUp(self, n, i/2); -// } -// } -// function _bubbleDown(Data storage self, Node memory n, uint i) private{// -// uint length = self.nodes.length; -// uint cIndex = i*2; // left child index -// -// if(length <= cIndex){ -// _insert(self, n, i); -// }else{ -// Node memory largestChild = self.nodes[cIndex]; -// -// if(length > cIndex+1 && self.nodes[cIndex+1].priority > largestChild.priority ){ -// largestChild = self.nodes[++cIndex];// TEST ++ gets executed first here -// } -// -// if(largestChild.priority <= n.priority){ //TEST: priority 0 is valid! negative ints work -// _insert(self, n, i); -// }else{ -// _insert(self, largestChild, i); -// _bubbleDown(self, n, cIndex); -// } -// } -// } -// -// function _insert(Data storage self, Node memory n, uint i) private{//√ -// self.nodes[i] = n; -// self.indices[n.id] = i; -// } -//} \ No newline at end of file +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +// Eth Heap +// Authors: Alexandre Tolstenko +// Original Author: Zac Mitton +// License: MIT +// ref.: https://github.com/zmitton/eth-heap/blob/master/contracts/Heap.sol + +library Heap { // default max-heap + uint constant ROOT_INDEX = 1; + + struct Data { + int128 idCount; + Node[] nodes; // root is index 1; index 0 not used + mapping (int128 => uint) indices; // unique id => node index + } + struct Node{ + int128 id; //use with another mapping to store arbitrary object types + int128 priority; + } + + //call init before anything else + function init(Data storage self) internal{ + if(self.nodes.length == 0) self.nodes.push(Node(0,0)); + } + + function insert(Data storage self, int128 priority) internal returns(Node memory){//√ + if(self.nodes.length == 0){ init(self); }// test on-the-fly-init + self.idCount++; +// self.nodes.length++; // this is the old approach. + self.nodes.push(Node(0,0)); // todo: check if it is really working + + Node memory n = Node(self.idCount, priority); + _bubbleUp(self, n, self.nodes.length-1); + return n; + } + function extractMax(Data storage self) internal returns(Node memory){//√ + return _extract(self, ROOT_INDEX); + } + function extractById(Data storage self, int128 id) internal returns(Node memory){//√ + return _extract(self, self.indices[id]); + } + + //view + function dump(Data storage self) internal view returns(Node[] memory){ + //note: Empty set will return `[Node(0,0)]`. uninitialized will return `[]`. + return self.nodes; + } + // todo: check all the storage and memory usage. Probably we will edit the values of a given node + function getById(Data storage self, int128 id) internal view returns(Node storage){ + return getByIndex(self, self.indices[id]);//test that all these return the emptyNode + } + function getByIndex(Data storage self, uint i) internal view returns(Node storage){ +// return self.nodes.length > i ? self.nodes[i] : Node(0,0); // old approach + require(self.nodes.length > i, "Invalid index"); // todo: test this + return self.nodes[i]; + } + function getMax(Data storage self) internal view returns(Node storage){ + return getByIndex(self, ROOT_INDEX); + } + function size(Data storage self) internal view returns(uint){ + return self.nodes.length > 0 ? self.nodes.length-1 : 0; + } + function isNode(Node memory n) internal pure returns(bool){ return n.id > 0; } + + //private + function _extract(Data storage self, uint i) private returns(Node memory){//√ + if(self.nodes.length <= i || i <= 0){ return Node(0,0); } + + Node memory extractedNode = self.nodes[i]; + delete self.indices[extractedNode.id]; + + Node memory tailNode = self.nodes[self.nodes.length-1]; +// self.nodes.length--; // old approach + self.nodes.pop(); // todo: test this + + if(i < self.nodes.length){ // if extracted node was not tail + _bubbleUp(self, tailNode, i); + _bubbleDown(self, self.nodes[i], i); // then try bubbling down + } + return extractedNode; + } + function _bubbleUp(Data storage self, Node memory n, uint i) private{//√ + if(i==ROOT_INDEX || n.priority <= self.nodes[i/2].priority){ + _insert(self, n, i); + }else{ + _insert(self, self.nodes[i/2], i); + _bubbleUp(self, n, i/2); + } + } + function _bubbleDown(Data storage self, Node memory n, uint i) private{// + uint length = self.nodes.length; + uint cIndex = i*2; // left child index + + if(length <= cIndex){ + _insert(self, n, i); + }else{ + Node memory largestChild = self.nodes[cIndex]; + + if(length > cIndex+1 && self.nodes[cIndex+1].priority > largestChild.priority ){ + largestChild = self.nodes[++cIndex];// TEST ++ gets executed first here + } + + if(largestChild.priority <= n.priority){ //TEST: priority 0 is valid! negative ints work + _insert(self, n, i); + }else{ + _insert(self, largestChild, i); + _bubbleDown(self, n, cIndex); + } + } + } + + function _insert(Data storage self, Node memory n, uint i) private{//√ + self.nodes[i] = n; + self.indices[n.id] = i; + } +} \ No newline at end of file