-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contract redesign #931
base: main
Are you sure you want to change the base?
Contract redesign #931
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,7 @@ pragma solidity ^0.8.23; | |
enum ConsensusType { | ||
Fendermint | ||
} | ||
|
||
enum Consensus { | ||
ProofOfPower | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.23; | ||
|
||
/// @notice A interface that indicated the implementing facet contains a or multiple genesis settings. | ||
interface IGenesisComponent { | ||
/// @notice Returns the id of the component | ||
function id() external view returns(bytes4); | ||
|
||
/// @notice Returns the actual bytes of the genesis | ||
function genesis() external view returns(bytes memory); | ||
|
||
/// @notice Checks if the component is bootstrapped | ||
function bootstrapped() external view returns(bool); | ||
Comment on lines
+12
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is better placed in a Lifecycle facet and generalized as a |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.23; | ||
|
||
/// @title Subnet interface | ||
interface ISubnet { | ||
/// @notice Checks if the subnet is now bootstrapped | ||
function bootstrapped() external view returns(bool); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate on what responsibility/scope this interface would hold going forward? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.23; | ||
|
||
import {EnumerableMap} from "openzeppelin-contracts/utils/structs/EnumerableMap.sol"; | ||
import {IGenesisComponent} from "../interfaces/IGenesis.sol"; | ||
|
||
struct SubnetGenesis { | ||
/// @notice The total circulation supply of the subnet | ||
uint256 circSupply; | ||
/// @notice The genesis balances of the address | ||
EnumerableMap.AddressToUintMap balances; | ||
} | ||
|
||
/// @title Lib Subnet Genesis | ||
/// @notice Handles the subnet genesis states and util functions | ||
library LibSubnetGenesis { | ||
using EnumerableMap for EnumerableMap.AddressToUintMap; | ||
|
||
/// @notice Deposit into the genesis balance of the address | ||
function deposit(SubnetGenesis storage self, address addr, uint256 amount) internal { | ||
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This OOP-style pattern is nice, since using LibSubnetGenesis for SubnetGenesis;
genesis.deposit(addr, amount); |
||
(bool exists, uint256 existingAmount) = self.balances.tryGet(addr); | ||
|
||
if (exists) { | ||
self.balances.set(addr, existingAmount + amount); | ||
} else { | ||
self.balances.set(addr, amount); | ||
} | ||
|
||
self.circSupply += amount; | ||
Comment on lines
+23
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be simplified by doing the addition to circSupply sooner, and using an early return in the if. But it's probably clearer this way. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.23; | ||
|
||
import {ProofOfPower, LibPowerQuery} from "./LibPower.sol"; | ||
import {PQ, LibPQ} from "./LibPQ.sol"; | ||
|
||
struct MaxPQ { | ||
PQ inner; | ||
} | ||
|
||
/// The max index priority queue for staking. The same implementation as LibMinPQ, just order compare | ||
/// is reversed. | ||
library LibMaxPQ { | ||
using LibPQ for PQ; | ||
using LibPowerQuery for ProofOfPower; | ||
|
||
function getSize(MaxPQ storage self) internal view returns (uint16) { | ||
return self.inner.size; | ||
} | ||
|
||
function getAddress(MaxPQ storage self, uint16 i) internal view returns (address) { | ||
return self.inner.posToAddress[i]; | ||
} | ||
|
||
function contains(MaxPQ storage self, address validator) internal view returns (bool) { | ||
return self.inner.contains(validator); | ||
} | ||
|
||
/// @notice Insert the validator address into this PQ. | ||
/// NOTE that caller should ensure the valdiator is not already in the queue. | ||
function insert(MaxPQ storage self, ProofOfPower storage proofOrPower, address validator) internal { | ||
uint16 size = self.inner.size + 1; | ||
|
||
self.inner.addressToPos[validator] = size; | ||
self.inner.posToAddress[size] = validator; | ||
|
||
self.inner.size = size; | ||
|
||
uint256 power = proofOrPower.getConfirmedPower(validator); | ||
swim({self: self, proofOrPower: proofOrPower, pos: size, value: power}); | ||
} | ||
|
||
/// @notice Pop the maximum value in the priority queue. | ||
/// NOTE that caller should ensure the queue is not empty! | ||
function pop(MaxPQ storage self, ProofOfPower storage proofOrPower) internal { | ||
self.inner.requireNotEmpty(); | ||
|
||
uint16 size = self.inner.size; | ||
|
||
self.inner.exchange(1, size); | ||
|
||
self.inner.size = size - 1; | ||
self.inner.del(size); | ||
|
||
uint256 power = self.inner.getPower(proofOrPower, 1); | ||
sink({self: self, proofOrPower: proofOrPower, pos: 1, value: power}); | ||
} | ||
|
||
/// @notice Reheapify the heap when the validator is deleted. | ||
/// NOTE that caller should ensure the queue is not empty. | ||
function deleteReheapify(MaxPQ storage self, ProofOfPower storage proofOrPower, address validator) internal { | ||
uint16 pos = self.inner.getPosOrRevert(validator); | ||
uint16 size = self.inner.size; | ||
|
||
self.inner.exchange(pos, size); | ||
|
||
// remove the item | ||
self.inner.size = size - 1; | ||
self.inner.del(size); | ||
|
||
if (size == pos) { | ||
return; | ||
} | ||
|
||
// swim pos up in case exchanged index is smaller | ||
uint256 power = self.inner.getPower(proofOrPower, pos); | ||
swim({self: self, proofOrPower: proofOrPower, pos: pos, value: power}); | ||
|
||
// sink pos down in case updated pos is larger | ||
power = self.inner.getPower(proofOrPower, pos); | ||
sink({self: self, proofOrPower: proofOrPower, pos: pos, value: power}); | ||
} | ||
|
||
/// @notice Reheapify the heap when the collateral of a key has increased. | ||
/// NOTE that caller should ensure the queue is not empty. | ||
function increaseReheapify(MaxPQ storage self, ProofOfPower storage proofOrPower, address validator) internal { | ||
uint16 pos = self.inner.getPosOrRevert(validator); | ||
uint256 power = proofOrPower.getConfirmedPower(validator); | ||
swim({self: self, proofOrPower: proofOrPower, pos: pos, value: power}); | ||
} | ||
|
||
/// @notice Reheapify the heap when the collateral of a key has decreased. | ||
/// NOTE that caller should ensure the queue is not empty. | ||
function decreaseReheapify(MaxPQ storage self, ProofOfPower storage proofOrPower, address validator) internal { | ||
uint16 pos = self.inner.getPosOrRevert(validator); | ||
uint256 power = proofOrPower.getConfirmedPower(validator); | ||
sink({self: self, proofOrPower: proofOrPower, pos: pos, value: power}); | ||
} | ||
|
||
/// @notice Get the maximum value in the priority queue. | ||
/// NOTE that caller should ensure the queue is not empty! | ||
function max(MaxPQ storage self, ProofOfPower storage proofOrPower) internal view returns (address, uint256) { | ||
self.inner.requireNotEmpty(); | ||
|
||
address addr = self.inner.posToAddress[1]; | ||
uint256 power = proofOrPower.getConfirmedPower(addr); | ||
return (addr, power); | ||
} | ||
|
||
/*************************************************************************** | ||
* Heap internal helper functions, should not be called by external functions | ||
****************************************************************************/ | ||
function swim(MaxPQ storage self, ProofOfPower storage proofOrPower, uint16 pos, uint256 value) internal { | ||
uint16 parentPos; | ||
uint256 parentPower; | ||
|
||
while (pos > 1) { | ||
parentPos = pos >> 1; // parentPos = pos / 2 | ||
parentPower = self.inner.getPower(proofOrPower, parentPos); | ||
|
||
// Parent power is not smaller than that of the current child, and the heap condition met. | ||
if (!firstValueSmaller(parentPower, value)) { | ||
break; | ||
} | ||
|
||
self.inner.exchange(parentPos, pos); | ||
pos = parentPos; | ||
} | ||
} | ||
|
||
function sink(MaxPQ storage self, ProofOfPower storage proofOrPower, uint16 pos, uint256 value) internal { | ||
uint16 childPos = pos << 1; // childPos = pos * 2 | ||
uint256 childPower; | ||
|
||
uint16 size = self.inner.size; | ||
|
||
while (childPos <= size) { | ||
if (childPos < size) { | ||
// select the max of the two children | ||
(childPos, childPower) = largerPosition({ | ||
self: self, | ||
proofOrPower: proofOrPower, | ||
pos1: childPos, | ||
pos2: childPos + 1 | ||
}); | ||
} else { | ||
childPower = self.inner.getPower(proofOrPower, childPos); | ||
} | ||
|
||
// parent, current idx, is not more than its two children, min heap condition is met. | ||
if (!firstValueSmaller(value, childPower)) { | ||
break; | ||
} | ||
|
||
self.inner.exchange(childPos, pos); | ||
pos = childPos; | ||
childPos = pos << 1; | ||
} | ||
} | ||
|
||
/// @notice Get the larger index of pos1 and pos2. | ||
function largerPosition( | ||
MaxPQ storage self, | ||
ProofOfPower storage proofOrPower, | ||
uint16 pos1, | ||
uint16 pos2 | ||
) internal view returns (uint16, uint256) { | ||
uint256 power1 = self.inner.getPower(proofOrPower, pos1); | ||
uint256 power2 = self.inner.getPower(proofOrPower, pos2); | ||
|
||
if (firstValueSmaller(power1, power2)) { | ||
return (pos2, power2); | ||
} | ||
return (pos1, power1); | ||
} | ||
|
||
function firstValueSmaller(uint256 v1, uint256 v2) internal pure returns (bool) { | ||
return v1 < v2; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly is ProofOfPower?