Skip to content

Commit

Permalink
merge multiquorums
Browse files Browse the repository at this point in the history
  • Loading branch information
gpsanant committed Aug 3, 2023
2 parents 6c2df41 + c865dc6 commit 56bea26
Show file tree
Hide file tree
Showing 49 changed files with 2,614 additions and 1,223 deletions.
22 changes: 11 additions & 11 deletions certora/applyHarness.patch
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
diff -druN ../score/DelegationManager.sol core/DelegationManager.sol
--- ../score/DelegationManager.sol 2023-01-13 14:12:34
+++ core/DelegationManager.sol 2023-01-13 14:24:43
@@ -160,10 +160,10 @@
--- ../score/DelegationManager.sol 2023-07-31 15:26:59
+++ core/DelegationManager.sol 2023-07-31 15:49:22
@@ -209,8 +209,11 @@
* Called by the StrategyManager whenever shares are decremented from a user's share balance, for example when a new withdrawal is queued.
* @dev Callable only by the StrategyManager.
*/
function decreaseDelegatedShares(
address staker,
- IStrategy[] calldata strategies,
- uint256[] calldata shares
+ IStrategy[] memory strategies, // MUNGED calldata => memory
+ uint256[] memory shares // MUNGED calldata => memory
)
- function decreaseDelegatedShares(address staker, IStrategy[] calldata strategies, uint256[] calldata shares)
- external
+ public // MUNGED external => public
+ function decreaseDelegatedShares(
+ // MUNGED calldata => memory
+ address staker, IStrategy[] memory strategies, uint256[] memory shares)
+ // MUNGED external => public
+ public
onlyStrategyManager
{
if (isDelegated(staker)) {
54 changes: 30 additions & 24 deletions certora/specs/core/DelegationManager.spec
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ methods {
function _.withdraw(address,address,uint256) external => DISPATCHER(true);

// external calls to EigenPodManager
function _.withdrawBeaconChainETH(address,address,uint256) external => DISPATCHER(true);
function _.withdrawRestakedBeaconChainETH(address,address,uint256) external => DISPATCHER(true);

// external calls to EigenPod
function _.withdrawBeaconChainETH(address,uint256) external => DISPATCHER(true);
function _.withdrawRestakedBeaconChainETH(address,uint256) external => DISPATCHER(true);

// external calls to PauserRegistry
function _.pauser() external => DISPATCHER(true);
function _.isPauser(address) external => DISPATCHER(true);
function _.unpauser() external => DISPATCHER(true);

// external calls to ERC1271 (can import OpenZeppelin mock implementation)
Expand All @@ -41,20 +41,24 @@ methods {
function _._delegationWithdrawnHook(address,address,address[]memory, uint256[] memory) internal => NONDET;
//envfree functions
function isDelegated(address staker) external returns (bool) envfree;
function isNotDelegated(address staker) external returns (bool) envfree;
function isOperator(address operator) external returns (bool) envfree;
function delegatedTo(address staker) external returns (address) envfree;
function delegationTerms(address operator) external returns (address) envfree;
function operatorDetails(address operator) external returns (IDelegationManager.OperatorDetails memory) envfree;
function earningsReceiver(address operator) external returns (address) envfree;
function delegationApprover(address operator) external returns (address) envfree;
function stakerOptOutWindowBlocks(address operator) external returns (uint256) envfree;
function operatorShares(address operator, address strategy) external returns (uint256) envfree;
function isDelegated(address staker) external returns (bool) envfree;
function isOperator(address operator) external returns (bool) envfree;
function stakerNonce(address staker) external returns (uint256) envfree;
function delegationApproverNonce(address delegationApprover) external returns (uint256) envfree;
function owner() external returns (address) envfree;
function strategyManager() external returns (address) envfree;
}

/*
LEGAL STATE TRANSITIONS:
1)
FROM not delegated -- defined as delegatedTo(staker) == address(0), likewise returned by isNotDelegated(staker)--
FROM not delegated -- defined as delegatedTo(staker) == address(0), likewise returned by !isDelegated(staker)--
AND not registered as an operator -- defined as isOperator(operator) == false, or equivalently, delegationTerms(operator) == 0,
TO delegated but not an operator
in this case, the end state is that:
Expand Down Expand Up @@ -100,7 +104,7 @@ FORBIDDEN STATES:
Combining the above, an address can be (classified as an operator) *iff* they are (delegated to themselves).
The exception is the zero address, since by default an address is 'delegated to the zero address' when they are not delegated at all
*/
//definition notDelegated -- defined as delegatedTo(staker) == address(0), likewise returned by isNotDelegated(staker)--
//definition notDelegated -- defined as delegatedTo(staker) == address(0), likewise returned by !isDelegated(staker)--

// verify that anyone who is registered as an operator is also always delegated to themselves
invariant operatorsAlwaysDelegatedToSelf(address operator)
Expand Down Expand Up @@ -156,39 +160,41 @@ rule cannotChangeDelegationWithoutUndelegating(address staker) {
rule canOnlyDelegateWithSpecificFunctions(address staker) {
requireInvariant operatorsAlwaysDelegatedToSelf(staker);
// assume the staker begins as undelegated
require(isNotDelegated(staker));
require(!isDelegated(staker));
// perform arbitrary function call
method f;
env e;
if (f.selector == sig:delegateTo(address).selector) {
if (f.selector == sig:delegateTo(address, IDelegationManager.SignatureWithExpiry).selector) {
address operator;
delegateTo(e, operator);
IDelegationManager.SignatureWithExpiry approverSignatureAndExpiry;
delegateTo(e, operator, approverSignatureAndExpiry);
// we check against operator being the zero address here, since we view being delegated to the zero address as *not* being delegated
if (e.msg.sender == staker && isOperator(operator) && operator != 0) {
assert (isDelegated(staker) && delegatedTo(staker) == operator, "failure in delegateTo");
} else {
assert (isNotDelegated(staker), "staker delegated to inappropriate address?");
assert (!isDelegated(staker), "staker delegated to inappropriate address?");
}
} else if (f.selector == sig:delegateToBySignature(address, address, uint256, bytes).selector) {
} else if (f.selector == sig:delegateToBySignature(address, address, IDelegationManager.SignatureWithExpiry, IDelegationManager.SignatureWithExpiry).selector) {
address toDelegateFrom;
address operator;
uint256 expiry;
bytes signature;
delegateToBySignature(e, toDelegateFrom, operator, expiry, signature);
IDelegationManager.SignatureWithExpiry stakerSignatureAndExpiry;
IDelegationManager.SignatureWithExpiry approverSignatureAndExpiry;
delegateToBySignature(e, toDelegateFrom, operator, stakerSignatureAndExpiry, approverSignatureAndExpiry);
// TODO: this check could be stricter! need to filter when the block timestamp is appropriate for expiry and signature is valid
assert (isNotDelegated(staker) || delegatedTo(staker) == operator, "delegateToBySignature bug?");
} else if (f.selector == sig:registerAsOperator(address).selector) {
address delegationTerms;
registerAsOperator(e, delegationTerms);
if (e.msg.sender == staker && delegationTerms != 0) {
assert (!isDelegated(staker) || delegatedTo(staker) == operator, "delegateToBySignature bug?");
} else if (f.selector == sig:registerAsOperator(IDelegationManager.OperatorDetails, string).selector) {
IDelegationManager.OperatorDetails operatorDetails;
string metadataURI;
registerAsOperator(e, operatorDetails, metadataURI);
if (e.msg.sender == staker) {
assert (isOperator(staker));
} else {
assert(isNotDelegated(staker));
assert(!isDelegated(staker));
}
} else {
calldataarg arg;
f(e,arg);
assert (isNotDelegated(staker), "staker became delegated through inappropriate function call");
assert (!isDelegated(staker), "staker became delegated through inappropriate function call");
}
}
Expand Down
12 changes: 4 additions & 8 deletions certora/specs/core/Slasher.spec
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,13 @@ methods {
function _.withdraw(address,address,uint256) external => DISPATCHER(true);

// external calls to EigenPodManager
function _.withdrawBeaconChainETH(address,address,uint256) external => DISPATCHER(true);
function _.withdrawRestakedBeaconChainETH(address,address,uint256) external => DISPATCHER(true);

// external calls to EigenPod
function _.withdrawBeaconChainETH(address,uint256) external => DISPATCHER(true);

// external calls to IDelegationTerms
function _.onDelegationWithdrawn(address,address[],uint256[]) external => CONSTANT;
function _.onDelegationReceived(address,address[],uint256[]) external => CONSTANT;

function _.withdrawRestakedBeaconChainETH(address,uint256) external => DISPATCHER(true);

// external calls to PauserRegistry
function _.pauser() external => DISPATCHER(true);
function _.isPauser(address) external => DISPATCHER(true);
function _.unpauser() external => DISPATCHER(true);

//// Harnessed Functions
Expand Down
6 changes: 1 addition & 5 deletions certora/specs/core/StrategyManager.spec
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,8 @@ methods {
// external calls to DelayedWithdrawalRouter (from EigenPod)
function _.createDelayedWithdrawal(address, address) external => DISPATCHER(true);

// external calls to IDelegationTerms
function _.onDelegationWithdrawn(address,address[],uint256[]) external => CONSTANT;
function _.onDelegationReceived(address,address[],uint256[]) external => CONSTANT;

// external calls to PauserRegistry
function _.pauser() external => DISPATCHER(true);
function _.isPauser(address) external => DISPATCHER(true);
function _.unpauser() external => DISPATCHER(true);

// external calls to ERC20
Expand Down
2 changes: 1 addition & 1 deletion certora/specs/permissions/Pausable.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

methods {
// external calls to PauserRegistry
function _.pauser() external => DISPATCHER(true);
function _.isPauser(address) external => DISPATCHER(true);
function _.unpauser() external => DISPATCHER(true);

// envfree functions
Expand Down
2 changes: 1 addition & 1 deletion certora/specs/strategies/StrategyBase.spec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ methods {
function _.stakerStrategyShares(address, address) external => DISPATCHER(true);

// external calls to PauserRegistry
function _.pauser() external => DISPATCHER(true);
function _.isPauser(address) external => DISPATCHER(true);
function _.unpauser() external => DISPATCHER(true);

// external calls to ERC20
Expand Down
7 changes: 4 additions & 3 deletions docs/EigenLayer-delegation-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ When an operator registers in EigenLayer, the following flow of calls between co

![Registering as an Operator in EigenLayer](images/EL_operator_registration.png?raw=true "Registering as an Operator in EigenLayer")

1. The would-be operator calls `DelegationManager.registerAsOperator`, providing either a `DelegationTerms`-type contract or an EOA as input. The DelegationManager contract stores the `DelegationTerms`-type contract provided by the operator, which may act as an intermediary to help facilitate the relationship between the operator and any stakers who delegate to them.
All of the remaining steps (2-4) proceed as outlined in the delegation process below; the DelegationManager contract treats things as if the operator has delegated *to themselves*.
1. The would-be operator calls `DelegationManager.registerAsOperator`, providing their `OperatorDetails` and an (optional) `metadataURI` string as an input. The DelegationManager contract stores the `OperatorDetails` provided by the operator and emits an event containing the `metadataURI`. The `OperatorDetails` help define the terms of the relationship between the operator and any stakers who delegate to them, and the `metadataURI` can provide additional details about the operator.
All of the remaining steps (2 and 3) proceed as outlined in the delegation process below; the DelegationManager contract treats things as if the operator has delegated *to themselves*.

## Staker Delegation

Expand All @@ -28,4 +28,5 @@ In either case, the end result is the same, and the flow of calls between contra
1. As outlined above, either the staker themselves calls `DelegationManager.delegateTo`, or the operator (or a third party) calls `DelegationManager.delegateToBySignature`, in which case the DelegationManager contract verifies the provided ECDSA signature
2. The DelegationManager contract calls `Slasher.isFrozen` to verify that the operator being delegated to is not frozen
3. The DelegationManager contract calls `StrategyManager.getDeposits` to get the full list of the staker (who is delegating)'s deposits. It then increases the delegated share amounts of operator (who is being delegated to) appropriately
4. The DelegationManager contract makes a call into the operator's stored `DelegationTerms`-type contract, calling the `onDelegationReceived` function to inform it of the new delegation

TODO: complete explanation of signature-checking. For the moment, you can look at the IDelegationManager interface or the DelegationManager contract itself for more details on this.
4 changes: 2 additions & 2 deletions docs/EigenLayer-tech-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ OR

2. a **delegator**, choosing to allow an operator to use their restaked assets in securing applications built on EigenLayer

Stakers can choose which path they’d like to take by interacting with the DelegationManager contract. Stakers who wish to delegate select an operator whom they trust to use their restaked assets to serve applications, while operators register to allow others to delegate to them, specifying a `DelegationTerms`-type contract (or EOA) which receives the funds they earn and can potentially help to mediate their relationship with any stakers who delegate to them.
Stakers can choose which path they’d like to take by interacting with the DelegationManager contract. Stakers who wish to delegate select an operator whom they trust to use their restaked assets to serve applications, while operators register to allow others to delegate to them, specifying their `OperatorDetails` and (optionally) providing a `metadataURI` to help structure and explain their relationship with any stakers who delegate to them.

#### Storage in DelegationManager

The `DelegationManager` contract relies heavily upon the `StrategyManager` contract. It keeps track of all active operators -- specifically by storing the `Delegation Terms` for each operator -- as well as storing what operator each staker is delegated to.
A **staker** becomes an **operator** by calling `registerAsOperator`. Once registered as an operator, the mapping entry `delegationTerms[operator]` is set **irrevocably** -- in fact we define someone as an operator if `delegationTerms[operator]` returns a nonzero address. Querying `delegationTerms(operator)` returns a `DelegationTerms`-type contract; however, the returned address may be an EOA, in which case the operator is assumed to handle payments through more "trusted" means, such as by doing off-chain computations and separate distributions.
A **staker** becomes an **operator** by calling `registerAsOperator`. By design, registered as an operator, an address can never "deregister" as an operator in EigenLayer.
The mapping `delegatedTo` stores which operator each staker is delegated to. Querying `delegatedTo(staker)` will return the *address* of the operator that `staker` is delegated to. Note that operators are *always considered to be delegated to themselves*.

DelegationManager defines when an operator is delegated or not, as well as defining what makes someone an operator:
Expand Down
Binary file modified docs/images/EL_delegating.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion script/BecomeOperator.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import "./EigenLayerParser.sol";
contract BecomeOperator is Script, DSTest, EigenLayerParser {
//performs basic deployment before each test
function run() external {
IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({
earningsReceiver: msg.sender,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
});
parseEigenLayerParams();
vm.broadcast(msg.sender);
delegation.registerAsOperator(IDelegationTerms(msg.sender));
string memory emptyStringForMetadataURI;
delegation.registerAsOperator(operatorDetails, emptyStringForMetadataURI);
}
}
3 changes: 2 additions & 1 deletion script/DepositAndDelegate.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ contract DepositAndDelegate is Script, DSTest, EigenLayerParser {
strategyManager.depositIntoStrategy(eigenStrat, eigen, wethAmount);
weth.approve(address(strategyManager), wethAmount);
strategyManager.depositIntoStrategy(wethStrat, weth, wethAmount);
delegation.delegateTo(dlnAddr);
IDelegationManager.SignatureWithExpiry memory signatureWithExpiry;
delegation.delegateTo(dlnAddr, signatureWithExpiry);
vm.stopBroadcast();
}
}
3 changes: 2 additions & 1 deletion script/whitelist/Staker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ contract Staker is Ownable {
) Ownable() {
token.approve(address(strategyManager), type(uint256).max);
strategyManager.depositIntoStrategy(strategy, token, amount);
delegation.delegateTo(operator);
IDelegationManager.SignatureWithExpiry memory signatureWithExpiry;
delegation.delegateTo(operator, signatureWithExpiry);
}

function callAddress(address implementation, bytes memory data) external onlyOwner returns(bytes memory) {
Expand Down
Loading

0 comments on commit 56bea26

Please sign in to comment.