Skip to content
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

feat: fixed accounting bug and refactoring #767

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 36 additions & 49 deletions src/contracts/core/StakeRootCompendium.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
address _owner,
address _rootConfirmer,
uint32 _proofIntervalSeconds,
uint96 _maxTotalCharge,
uint96 _maxChargePerProof,
uint96 _chargePerStrategy,
uint96 _chargePerOperatorSet
) public initializer {
Expand All @@ -47,7 +47,7 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
chargeParams = ChargeParams({
chargePerOperatorSet: _chargePerOperatorSet,
chargePerStrategy: _chargePerStrategy,
maxTotalCharge: _maxTotalCharge
maxChargePerProof: _maxChargePerProof
});

cumulativeChargeParams.proofIntervalSeconds = _proofIntervalSeconds;
Expand All @@ -66,7 +66,7 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
DepositInfo storage depositInfo = depositInfos[operatorSet.avs][operatorSet.operatorSetId];
if (!_isInStakeTree(operatorSet)) {
(, uint256 cumulativeChargePerOperatorSet, uint256 cumulativeChargePerStrategy) =
_calculateCumulativeCharges(chargeParams, cumulativeChargeParams);
_calculateCumulativeCharges();

depositInfo.lastDemandIncreaseTimestamp = uint32(block.timestamp);
depositInfo.cumulativeChargePerOperatorSetLastPaid = uint96(cumulativeChargePerOperatorSet);
Expand Down Expand Up @@ -226,13 +226,13 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
TimestampAlreadyPosted()
);
// credit the charge recipient
Snapshots.Snapshot memory _snapshot = totalChargeHistory._snapshots[indexChargePerProof];
Snapshots.Snapshot memory _snapshot = chargePerProof._snapshots[indexChargePerProof];
require(
_snapshot._key <= calculationTimestamp, TimestampOfIndexChargePerProofIsGreaterThanCalculationTimestamp()
);
require(
totalChargeHistory.length() == indexChargePerProof + 1
|| uint256(totalChargeHistory._snapshots[indexChargePerProof + 1]._key) > calculationTimestamp,
chargePerProof.length() == indexChargePerProof + 1
|| uint256(chargePerProof._snapshots[indexChargePerProof + 1]._key) > calculationTimestamp,
IndexChargePerProofNotValid()
);
stakeRootSubmissions.push(
Expand Down Expand Up @@ -262,35 +262,33 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
/// SET FUNCTIONS

/// @inheritdoc IStakeRootCompendium
function setMaxTotalCharge(
uint96 _maxTotalCharge
function setMaxChargePerProof(
uint96 _maxChargePerProof
) external onlyOwner {
require(_maxTotalCharge >= totalChargeHistory.latest(), MaxTotalChargeMustBeGreaterThanTheCurrentTotalCharge());
chargeParams.maxTotalCharge = _maxTotalCharge;
require(_maxChargePerProof >= chargePerProof.latest(), MaxTotalChargeMustBeGreaterThanTheCurrentTotalCharge());
chargeParams.maxChargePerProof = _maxChargePerProof;
}

/// @inheritdoc IStakeRootCompendium
function setChargePerProof(uint96 _chargePerStrategy, uint96 _chargePerOperatorSet) external onlyOwner {
ChargeParams storage charges = chargeParams;
_updateTotalCharge(charges);
charges.chargePerStrategy = _chargePerStrategy;
charges.chargePerOperatorSet = _chargePerOperatorSet;
_updateTotalCharge(charges);
_updateCumulativeCharge();
chargeParams.chargePerStrategy = _chargePerStrategy;
chargeParams.chargePerOperatorSet = _chargePerOperatorSet;
_updateChargePerProof();
}

/// @inheritdoc IStakeRootCompendium
function setProofIntervalSeconds(
uint32 proofIntervalSeconds
) external onlyOwner {
CumulativeChargeParams storage cumulativeCharges = cumulativeChargeParams;
_updateTotalCharge(chargeParams);
_updateCumulativeCharge();
// we must not interrupt pending proof calculations by rugging the outstanding calculationTimestamps
require(
stakeRootSubmissions[stakeRootSubmissions.length - 1].calculationTimestamp
== cumulativeCharges.lastUpdateTimestamp,
== cumulativeChargeParams.lastUpdateTimestamp,
NoProofsThatHaveBeenChargedButNotSubmitted()
);
cumulativeCharges.proofIntervalSeconds = proofIntervalSeconds;
cumulativeChargeParams.proofIntervalSeconds = proofIntervalSeconds;
}

/// @inheritdoc IStakeRootCompendium
Expand Down Expand Up @@ -326,48 +324,43 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {

function _updateTotalStrategies(uint256 _countStrategiesBefore, uint256 _countStrategiesAfter) internal {
totalStrategies = totalStrategies - _countStrategiesBefore + _countStrategiesAfter;
_updateTotalCharge(chargeParams);
_updateChargePerProof();
}

function _updateTotalCharge(
ChargeParams memory charges
) internal {
function _updateChargePerProof() internal {
// note if totalStrategies is 0, the charge per proof will be 0, and provers should not post a proof
uint256 totalCharge =
operatorSets.length * charges.chargePerOperatorSet + totalStrategies * charges.chargePerStrategy;
require(totalCharge <= charges.maxTotalCharge, ChargePerProofExceedsMaxTotalCharge());
totalChargeHistory.push(uint32(block.timestamp), uint224(totalCharge));
uint256 _chargePerProof =
operatorSets.length * chargeParams.chargePerOperatorSet + totalStrategies * chargeParams.chargePerStrategy;
require(_chargePerProof <= chargeParams.maxChargePerProof, ChargePerProofExceedsMax());
chargePerProof.push(uint32(block.timestamp), uint224(_chargePerProof));
}

function _calculateCumulativeCharges(
ChargeParams memory charges,
CumulativeChargeParams memory cumulativeCharges
) internal view returns (uint32, uint96, uint96) {
function _calculateCumulativeCharges() internal view returns (uint32, uint96, uint96) {
// calculate the total charge since the last update up until the latest calculation timestamp
// note that there may be no corresponding stakeRootSubmission for the latest calculation timestamp
// but if the calculationTimestamp is in the past, then it should be charged for, since proofs are being generated
uint32 latestCalculationTimestamp =
uint32(block.timestamp) - uint32(block.timestamp % cumulativeCharges.proofIntervalSeconds);
uint32(block.timestamp) - uint32(block.timestamp % cumulativeChargeParams.proofIntervalSeconds);

if (cumulativeCharges.lastUpdateTimestamp == latestCalculationTimestamp) {
if (cumulativeChargeParams.lastUpdateTimestamp == latestCalculationTimestamp) {
return (
latestCalculationTimestamp, cumulativeCharges.chargePerOperatorSet, cumulativeCharges.chargePerStrategy
latestCalculationTimestamp, cumulativeChargeParams.chargePerOperatorSet, cumulativeChargeParams.chargePerStrategy
);
}

uint256 numProofs = (latestCalculationTimestamp - cumulativeCharges.lastUpdateTimestamp)
/ cumulativeCharges.proofIntervalSeconds;
uint256 numProofs = (latestCalculationTimestamp - cumulativeChargeParams.lastUpdateTimestamp)
/ cumulativeChargeParams.proofIntervalSeconds;

return (
latestCalculationTimestamp,
uint96(cumulativeCharges.chargePerOperatorSet + charges.chargePerOperatorSet * numProofs),
uint96(cumulativeCharges.chargePerStrategy + charges.chargePerStrategy * numProofs)
uint96(cumulativeChargeParams.chargePerOperatorSet + chargeParams.chargePerOperatorSet * numProofs),
uint96(cumulativeChargeParams.chargePerStrategy + chargeParams.chargePerStrategy * numProofs)
);
}

function _updateCumulativeCharge() internal {
(uint32 lastUpdateTimestamp, uint96 cumulativeChargePerOperatorSet, uint96 cumulativeChargePerStrategy) =
_calculateCumulativeCharges(chargeParams, cumulativeChargeParams);
_calculateCumulativeCharges();

cumulativeChargeParams = CumulativeChargeParams({
chargePerOperatorSet: cumulativeChargePerOperatorSet,
Expand All @@ -384,7 +377,7 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
require(_isInStakeTree(operatorSet), StakeTreeMustIncludeOperatorSet());

(, uint256 cumulativeChargePerOperatorSet, uint256 cumulativeChargePerStrategy) =
_calculateCumulativeCharges(chargeParams, cumulativeChargeParams);
_calculateCumulativeCharges();
DepositInfo storage depositInfo = depositInfos[operatorSet.avs][operatorSet.operatorSetId];

// subtract new total charge from last paid total charge
Expand Down Expand Up @@ -500,7 +493,7 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
) external view returns (uint256 balance) {
DepositInfo memory depositInfo = depositInfos[operatorSet.avs][operatorSet.operatorSetId];
(, uint96 cumulativeChargePerOperatorSet, uint96 cumulativeChargePerStrategy) =
_calculateCumulativeCharges(chargeParams, cumulativeChargeParams);
_calculateCumulativeCharges();
uint256 pendingCharge = uint256(
cumulativeChargePerOperatorSet - depositInfo.cumulativeChargePerOperatorSetLastPaid
)
Expand All @@ -519,7 +512,6 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {

/// @inheritdoc IStakeRootCompendium
function getStakeRoot(
address avs,
uint32[] calldata operatorSetIdsInStakeTree,
bytes32[] calldata operatorSetRoots
) external view returns (bytes32) {
Expand Down Expand Up @@ -609,13 +601,8 @@ contract StakeRootCompendium is StakeRootCompendiumStorage {
}

function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
(bool success, ) = to.call{value: amount}("");
require(success, EthTransferFailed());
}

// in case of charge problems
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/core/StakeRootCompendiumStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ abstract contract StakeRootCompendiumStorage is IStakeRootCompendium, OwnableUpg
/// @notice list of operator sets that have been configured to be in the StakeTree
OperatorSet[] public operatorSets;
/// @notice the total charge for a proofs at a certain time depending on the number of strategies
Snapshots.History internal totalChargeHistory;
Snapshots.History internal chargePerProof;
/// @dev Contains cumulative charges for operator sets, strategies, and max total charge.
ChargeParams public chargeParams;
/// @dev Contains info about cumulative charges.
Expand Down
13 changes: 6 additions & 7 deletions src/contracts/interfaces/IStakeRootCompendium.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface IStakeRootCompendium {
error TimestampAlreadyConfirmed();
error MaxTotalChargeMustBeGreaterThanTheCurrentTotalCharge();
error NoProofsThatHaveBeenChargedButNotSubmitted();
error ChargePerProofExceedsMaxTotalCharge();
error ChargePerProofExceedsMax();
error InputArrayLengthMismatch();
error InputCorrelatedVariableMismatch();
error OutOfBounds();
Expand All @@ -36,7 +36,7 @@ interface IStakeRootCompendium {
struct ChargeParams {
uint96 chargePerOperatorSet;
uint96 chargePerStrategy;
uint96 maxTotalCharge;
uint96 maxChargePerProof;
}

/// @dev Struct containing info about cumulative charges.
Expand Down Expand Up @@ -133,7 +133,6 @@ interface IStakeRootCompendium {
* @dev operatorSetRoots must be ordered by the operatorSet index at the time of call
*/
function getStakeRoot(
address avs,
uint32[] calldata operatorSetIdsInStakeTree,
bytes32[] calldata operatorSetRoots
) external view returns (bytes32);
Expand Down Expand Up @@ -282,13 +281,13 @@ interface IStakeRootCompendium {
) external view returns (uint256 balance);

/**
* @notice set the maximum total charge for a stakeRoot proof
* @param _maxTotalCharge the maximum total charge for a stakeRoot proof
* @notice set the maximum charge for a stakeRoot proof
* @param _maxChargePerProof the maximum charge for a stakeRoot proof
* @dev only callable by owner
* @dev used to limit offchain computation
*/
function setMaxTotalCharge(
uint96 _maxTotalCharge
function setMaxChargePerProof(
uint96 _maxChargePerProof
) external;

/**
Expand Down
Loading