Skip to content

Commit

Permalink
add(PrizeTierHistoryV2):optimized binary search
Browse files Browse the repository at this point in the history
  • Loading branch information
kamescg committed Mar 18, 2022
1 parent 0bf90de commit ed4433d
Show file tree
Hide file tree
Showing 7 changed files with 558 additions and 0 deletions.
110 changes: 110 additions & 0 deletions contracts/PrizeTierHistoryV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;
import "@pooltogether/owner-manager-contracts/contracts/Manageable.sol";
import "./abstract/IdBinarySearchLib.sol";

contract PrizeTierHistoryV2 is IdBinarySearchLib, Manageable {

struct PrizeTier {
uint8 bitRangeSize;
uint32 drawId;
uint32 maxPicksPerUser;
uint32 expiryDuration;
uint32 endTimestampOffset;
uint256 prize;
uint32[16] tiers;
}

uint32[] internal history;
mapping(uint32 => PrizeTier) internal prizeTiers;

/**
* @notice Emit when new PrizeTier is added to history
* @param drawId Draw ID
* @param prizeTier PrizeTier parameters
*/
event PrizeTierPushed(uint32 indexed drawId, PrizeTier prizeTier);

/**
* @notice Emit when existing PrizeTier is updated in history
* @param drawId Draw ID
* @param prizeTier PrizeTier parameters
*/
event PrizeTierSet(uint32 indexed drawId, PrizeTier prizeTier);

constructor(address owner, PrizeTier[] memory _history) Ownable(owner) {
if (_history.length > 0) {
inject(_history);
}
}

// @inheritdoc DrawIDBinarySearch
function getNewestIndex() internal view override returns (uint32) {
return uint32(history.length - 1);
}

// @inheritdoc DrawIDBinarySearch
function getIdForIndex(uint256 index) internal view override returns (uint32) {
return history[index];
}

function getPrizeTier(uint32 drawId) external view returns (PrizeTier memory) {
require(drawId > 0, "PrizeTierHistoryV2/draw-id-not-zero");
return prizeTiers[history[_binarySearch(drawId)]];
}

function getPrizeTierList(uint32[] calldata _drawIds) external view returns (PrizeTier[] memory) {
PrizeTier[] memory _data = new PrizeTier[](_drawIds.length);
for (uint256 index = 0; index < _drawIds.length; index++) {
_data[index] = prizeTiers[history[_binarySearch(_drawIds[index])]];
}
return _data;
}

function push(PrizeTier calldata nextPrizeTier) external onlyManagerOrOwner {
_push(nextPrizeTier);
}

function popAndPush(PrizeTier calldata newPrizeTier) external onlyManagerOrOwner {
uint length = history.length;
require(length > 0, "PrizeTierHistoryV2/history-empty");
require(history[length - 1] == newPrizeTier.drawId, "PrizeTierHistoryV2/invalid-draw-id");
_replace(newPrizeTier);
}

function replace(PrizeTier calldata newPrizeTier) external onlyOwner {
_replace(newPrizeTier);
}

function inject(PrizeTier[] memory timeline) public onlyOwner {
require(history.length == 0, "PrizeTierHistoryV2/history-not-empty");
require(timeline.length > 0, "PrizeTierHistoryV2/timeline-empty");
for (uint256 i = 0; i < timeline.length; i++) {
_push(timeline[i]);
}
}

function _push(PrizeTier memory _prizeTier) internal {
if (history.length > 0) {
uint32 _id = history[history.length - 1];
require(
_prizeTier.drawId > _id,
"PrizeTierHistoryV2/non-sequential-dpr"
);
}
history.push(_prizeTier.drawId);
prizeTiers[_prizeTier.drawId] = _prizeTier;
emit PrizeTierPushed(_prizeTier.drawId, _prizeTier);
}

function _replace(PrizeTier calldata _prizeTier) internal {
uint256 cardinality = history.length;
require(cardinality > 0, "PrizeTierHistoryV2/no-prize-tiers");
uint32 oldestDrawId = history[0];
require(_prizeTier.drawId >= oldestDrawId, "PrizeTierHistoryV2/draw-id-out-of-range");
uint32 index = _binarySearch(_prizeTier.drawId);
require(history[index] == _prizeTier.drawId, "PrizeTierHistoryV2/draw-id-must-match");
prizeTiers[_prizeTier.drawId] = _prizeTier;
emit PrizeTierSet(_prizeTier.drawId, _prizeTier);
}
}
22 changes: 22 additions & 0 deletions contracts/abstract/DrawIDBinarySearch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;

/**
* @title PoolTogether V4 DrawIDBinarySearch
* @author PoolTogether Inc Team
* @notice DrawIDBinarySearch uses binary search to find a parent contract struct with the drawId parameter
* @dev The implementing contract must provider access to a struct (i.e. PrizeTier) list with is both
* sorted and indexed by the drawId field for binary search to work.
*/
abstract contract DrawIDBinarySearch {
/**
* @notice Get newest index in array
*/
function getNewestIndex() internal view virtual returns (uint32);

/**
* @notice Get Draw ID for using an index position
* @param index uint256 - Index of element in array
*/
function getDrawIdForIndex(uint256 index) internal view virtual returns (uint32);
}
64 changes: 64 additions & 0 deletions contracts/abstract/IdBinarySearchLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;

/**
* @title PoolTogether V4 IdBinarySearchLib
* @author PoolTogether Inc Team
* @notice IdBinarySearchLib uses binary search to find a parent contract struct with the drawId parameter
* @dev The implementing contract must provider access to a struct (i.e. PrizeTier) list with is both
* sorted and indexed by the drawId field for binary search to work.
*/
abstract contract IdBinarySearchLib {
/**
* @notice Get newest index in array
*/
function getNewestIndex() internal view virtual returns (uint32);

/**
* @notice Get Draw ID for using an index position
* @param index uint256 - Index of element in array
*/
function getIdForIndex(uint256 index) internal view virtual returns (uint32);

function _binarySearch(uint32 _drawId) internal view returns (uint32) {
uint32 index;
uint32 leftSide = 0;
uint32 rightSide = getNewestIndex();

uint32 oldestDrawId = getIdForIndex(leftSide);
uint32 newestDrawId = getIdForIndex(rightSide);

require(_drawId >= oldestDrawId, "IdBinarySearchLib/draw-id-out-of-range");
if (_drawId >= newestDrawId) return rightSide;
if (_drawId == oldestDrawId) return leftSide;

while (true) {
uint32 length = rightSide - leftSide;
uint32 center = leftSide + (length) / 2;
uint32 centerID = getIdForIndex(center);

if (centerID == _drawId || length == 1) {
index = center;
break;
}

if (centerID < _drawId) {
leftSide = center;
} else if (centerID > _drawId) {
rightSide = center - 1;
}

if (leftSide == rightSide) {
if (centerID >= _drawId) {
index = center - 1;
break;
} else {
index = center;
break;
}
}
}

return index;
}
}
61 changes: 61 additions & 0 deletions contracts/test/DrawIDAndStructMappingBinarySearch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;
import "../abstract/IdBinarySearchLib.sol";

contract DrawIDAndStructMappingBinarySearch is IdBinarySearchLib {
struct Draw {
uint32 drawId;
uint256 randomNumber;
}

uint32[] internal history;
mapping(uint32 => Draw) internal draws;

constructor(Draw[] memory _history) {
if (_history.length > 0) {
inject(_history);
}
}

// @inheritdoc DrawIDBinarySearch
function getNewestIndex() internal view override returns (uint32) {
return uint32(history.length - 1);
}

// @inheritdoc DrawIDBinarySearch
function getIdForIndex(uint256 index) internal view override returns (uint32) {
return history[index];
}

function get(uint32 _drawId) external view returns (Draw memory) {
return draws[history[_binarySearch(_drawId)]];
}

function list(uint32[] calldata _drawIds) external view returns (Draw[] memory) {
Draw[] memory _data = new Draw[](_drawIds.length);
for (uint256 index = 0; index < _drawIds.length; index++) {
_data[index] = draws[history[_binarySearch(_drawIds[index])]];
}
return _data;
}

function inject(Draw[] memory _timeline) public {
require(history.length == 0, "DrawIDAndStructMappingBinarySearch/history-not-empty");
require(_timeline.length > 0, "DrawIDAndStructMappingBinarySearch/timeline-empty");
for (uint256 i = 0; i < _timeline.length; i++) {
_push(_timeline[i]);
}
}

function _push(Draw memory _draw) internal {
if (history.length > 0) {
uint32 _id = history[history.length - 1];
require(
_draw.drawId > _id,
"DrawIDAndStructMappingBinarySearch/non-sequential-dpr"
);
}
history.push(_draw.drawId);
draws[_draw.drawId] = _draw;
}
}
Binary file modified test/.DS_Store
Binary file not shown.

0 comments on commit ed4433d

Please sign in to comment.