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

Generalized Proofs #346

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
6b172db
init
Sidu28 Nov 28, 2023
ca2c983
added proof switch
Sidu28 Nov 28, 2023
b56976c
added proof switch turn on function
Sidu28 Nov 28, 2023
38dafc6
cleanup
Sidu28 Nov 29, 2023
0e3f0f1
cleanup
Sidu28 Nov 29, 2023
ceb5cb1
minor cleanup
Sidu28 Nov 29, 2023
d290f04
minor cleanup
Sidu28 Nov 29, 2023
59a328d
cleanup
Sidu28 Nov 29, 2023
a6ac41a
moved stuff to EPM
Sidu28 Nov 30, 2023
7613bea
added oracle root check
Sidu28 Nov 30, 2023
79d6950
fixed
Sidu28 Nov 30, 2023
258d95f
withdrawalProvenUntilTimestamp to mostRecentWithdrawalTimestamp
Sidu28 Nov 30, 2023
2096a24
removed maxFee
Sidu28 Nov 30, 2023
87f6de2
updated sumOfPartialWithdrawalsClaimedGwei
Sidu28 Nov 30, 2023
a907f7e
clean
Sidu28 Dec 5, 2023
7f45cac
added natspec
Sidu28 Dec 5, 2023
e25381c
Merge branch 'm2-mainnet' into partialpoc
Sidu28 Dec 5, 2023
4ce6725
code compiling
Sidu28 Dec 5, 2023
89db042
Merge branch 'partialpoc' of https://github.com/Layr-Labs/eigenlayer-…
Sidu28 Dec 5, 2023
64f6858
added functions to interface
Sidu28 Dec 5, 2023
673505f
added back tests
Dec 7, 2023
eb718a2
addressed comments
Dec 7, 2023
fc2c45e
fixed comparator
Sidu28 Dec 7, 2023
ab40644
bug fix, add non reentrant
Sidu28 Dec 7, 2023
fd6838a
addressed all changes
Sidu28 Dec 7, 2023
7fc43a5
addressed comments
Sidu28 Dec 7, 2023
2d5216e
fixed CEI
Sidu28 Dec 8, 2023
51721c0
added extra check of podAddress against podOwner
Sidu28 Dec 8, 2023
b281a62
testing
Sidu28 Dec 8, 2023
183dd32
switched wei to gwei for readability
Sidu28 Dec 9, 2023
1343c4d
enforce enough balance for fee
Sidu28 Dec 9, 2023
24c4d8a
added test for amounts
Sidu28 Dec 9, 2023
5407420
fixed breaking tests
Sidu28 Dec 12, 2023
0de6c2c
added verifier to EPM
Sidu28 Dec 14, 2023
2405226
everything compiling except EPM unit tests
Sidu28 Dec 14, 2023
cb9e679
everything compiling except EPM unit tests
Sidu28 Dec 14, 2023
75923d7
fixed abi.decode/encode, added tests
Sidu28 Dec 17, 2023
2e4b640
added check for verifier
Sidu28 Dec 17, 2023
e869b1e
removed abi.decode, added struct to input
Sidu28 Dec 21, 2023
336e245
removed abi.decode, added struct to input
Sidu28 Dec 21, 2023
914ada4
tests working
Sidu28 Dec 21, 2023
f91a038
fixed tests
Sidu28 Dec 21, 2023
8ea334d
addressed comments
Sidu28 Dec 21, 2023
dcf8a04
addressed comments
Sidu28 Dec 26, 2023
340a819
fixed missing if else statement
Sidu28 Dec 26, 2023
83c5347
Added length verification checks
Sidu28 Jan 3, 2024
4e1663c
tests compile
Sidu28 Jan 3, 2024
fd26c96
all tests working
Sidu28 Jan 3, 2024
eb5c18e
test building
Sidu28 Jan 6, 2024
e5054e1
fixed CEI and fee deduction logic
Sidu28 Jan 10, 2024
53a0c4c
fixed CEI and fee deduction logic
Sidu28 Jan 10, 2024
0720245
quick fix
Sidu28 Jan 10, 2024
39ea24d
moved pauser check from EP to EPM
Sidu28 Jan 10, 2024
a14f62b
extraneous check
Sidu28 Jan 10, 2024
f7d2b8e
added non revert test cases
Sidu28 Jan 10, 2024
c533844
Merge branch 'm2-mainnet' into partialpoc
Sidu28 Jan 10, 2024
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
4 changes: 0 additions & 4 deletions .husky/commit-msg

This file was deleted.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"fs": "^0.0.1-security",
"hardhat": "^2.12.4",
"hardhat-preprocessor": "^0.1.5",
"husky": "^8.0.3",
"ts-node": "^10.9.1",
"typescript": "^4.9.4",
"yargs": "^17.7.2"
Expand Down
16 changes: 15 additions & 1 deletion src/contracts/interfaces/IEigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ interface IEigenPod {
int256 sharesDeltaGwei;
}

struct VerifiedPartialWithdrawalBatch{
// amount being proven for withdrawal
uint64 provenPartialWithdrawalSumGwei;
// the latest timestamp proven until
uint64 mostRecentWithdrawalTimestamp;
// upper bound of the withdrawal period
uint64 endTimestamp;
ChaoticWalrus marked this conversation as resolved.
Show resolved Hide resolved
}


enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
REDEEMED,
Expand Down Expand Up @@ -94,7 +103,6 @@ interface IEigenPod {
/// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);


/// @notice The max amount of eth, in gwei, that can be restaked per validator
function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);

Expand Down Expand Up @@ -220,4 +228,10 @@ interface IEigenPod {

/// @notice called by owner of a pod to remove any ERC20s deposited in the pod
function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external;

function fulfillPartialWithdrawalProofRequest(
Sidu28 marked this conversation as resolved.
Show resolved Hide resolved
IEigenPod.VerifiedPartialWithdrawalBatch calldata verifiedPartialWithdrawalBatch,
uint64 feeGwei,
address feeRecipient
) external;
}
67 changes: 67 additions & 0 deletions src/contracts/interfaces/IEigenPodManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ interface IEigenPodManager is IPausable {
/// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue`
event MaxPodsUpdated(uint256 previousValue, uint256 newValue);

/// @notice Emitted when a new proof fulfiller is added
event ProofServiceUpdated(ProofService proofService);

/// @notice emitted when the partial withdrawal proof switch is turned on
event ProofServiceEnabled();

/// @notice Emitted when a withdrawal of beacon chain ETH is completed
event BeaconChainETHWithdrawalCompleted(
address indexed podOwner,
Expand All @@ -39,6 +45,51 @@ interface IEigenPodManager is IPausable {
bytes32 withdrawalRoot
);

//info for each withdrawal called back by proof service
struct WithdrawalCallbackInfo {
// oracle timestamp
uint64 oracleTimestamp;
// prover fee for each pod being proven for
uint64[] feesGwei;
/// @notice SNARK proof acting as the cryptographic seal over the execution results.
bytes seal;
/// @notice Digest of the zkVM SystemState after execution.
/// @dev The relay does not additionally check any property of this digest, but needs the
/// digest in order to reconstruct the ReceiptMetadata hash to which the proof is linked.
bytes32 postStateDigest;
//journal generated by offchain proof
Journal journal;
// imageID generated by offchain proof
bytes32 imageId;
}

struct Journal {
// amount being proven for withdrawal
uint64[] provenPartialWithdrawalSumsGwei;
// computed blockRoot
bytes32 blockRoot;
// the address of the pod being proven for
address[] podAddresses;
// the address of the pod owner
address[] podOwners;
// the latest timestamp proven until
uint64[] mostRecentWithdrawalTimestamps;
// upper bound of the withdrawal period
uint64[] endTimestamps;
gpsanant marked this conversation as resolved.
Show resolved Hide resolved
// user signed fee
uint64[] maxFeesGwei;
//request nonce
uint64 nonce;
}

struct ProofService {
address caller;
// fee recipient address of the proof service
address feeRecipient;
//address of the groth-16 verifier contract
address verifier;
}

/**
* @notice Creates an EigenPod for the sender.
* @dev Function will revert if the `msg.sender` already has an EigenPod.
Expand Down Expand Up @@ -143,4 +194,20 @@ interface IEigenPodManager is IPausable {
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;


/// @notice Returns the status of the proof service
function proofServiceEnabled() external view returns (bool);

/// @notice turns on offchain proof service
function enableProofService() external;

/// @notice updates the proof service caller
function updateProofService(ProofService calldata newProofService) external;

/// @notice callback for proof service
function proofServiceCallback(
WithdrawalCallbackInfo calldata callbackInfo
) external;

}
88 changes: 88 additions & 0 deletions src/contracts/interfaces/IRiscZeroVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2023 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.9;

/// @notice Indicator for the overall system at the end of execution covered by this proof.
enum SystemExitCode {
Halted,
Paused,
SystemSplit
}

/// @notice Combination of system and user exit codes.
/// @dev If system exit code is SystemSplit, the user exit code must be zero.
struct ExitCode {
SystemExitCode system;
uint8 user;
}

/// @notice Data associated with a receipt which is used for both input and
/// output of global state.
struct ReceiptMetadata {
/// Digest of the SystemState of a segment just before execution has begun.
bytes32 preStateDigest;
/// Digest of the SystemState of a segment just after execution has completed.
bytes32 postStateDigest;
/// The exit code for a segment
ExitCode exitCode;
/// A digest of the input, from the viewpoint of the guest.
bytes32 input;
/// A digest of the journal, from the viewpoint of the guest.
bytes32 output;
}

library ReceiptMetadataLib {
bytes32 constant TAG_DIGEST = sha256("risc0.ReceiptMeta");

function digest(ReceiptMetadata memory meta) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
meta.input,
meta.preStateDigest,
meta.postStateDigest,
meta.output,
// data
uint32(meta.exitCode.system) << 24,
uint32(meta.exitCode.user) << 24,
// down.length
uint16(4) << 8
)
);
}
}

struct Receipt {
bytes seal;
ReceiptMetadata meta;
}

interface IRiscZeroVerifier {
/// @notice verify that the given receipt is a valid Groth16 RISC Zero recursion receipt.
/// @return true if the receipt passes the verification checks.
function verify(Receipt calldata receipt) external view returns (bool);

/// @notice verifies that the given seal is a valid Groth16 RISC Zero proof of execution over the
/// given image ID, post-state digest, and journal. Asserts that the input hash
// is all-zeros (i.e. no committed input) and the exit code is (Halted, 0).
/// @return true if the receipt passes the verification checks.
function verify(bytes calldata seal, bytes32 imageId, bytes32 postStateDigest, bytes32 journalHash)
external
view
returns (bool);
}
54 changes: 51 additions & 3 deletions src/contracts/pods/EigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
uint256 public nonBeaconChainETHBalanceWei;

/// @notice This variable tracks the total amount of partial withdrawals claimed via merkle proofs prior to a switch to ZK proofs for claiming partial withdrawals
uint64 public sumOfPartialWithdrawalsClaimedGwei;
uint64 public sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei;

modifier onlyEigenPodManager() {
require(msg.sender == address(eigenPodManager), "EigenPod.onlyEigenPodManager: not eigenPodManager");
Expand Down Expand Up @@ -311,7 +311,6 @@
(validatorFieldsProofs.length == validatorFields.length),
"EigenPod.verifyWithdrawalCredentials: validatorIndices and proofs must be same length"
);

/**
* Withdrawal credential proof should not be "stale" (older than VERIFY_BALANCE_UPDATE_WINDOW_SECONDS) as we are doing a balance check here
* The validator container persists as the state evolves and even after the validator exits. So we can use a more "fresh" credential proof within
Expand Down Expand Up @@ -428,6 +427,49 @@
_sendETH(recipient, amountWei);
}


Sidu28 marked this conversation as resolved.
Show resolved Hide resolved
/*******************************************************************************
EXTERNAL FUNCTIONS CALLABLE BY PERMISSIONED SERVICES
*******************************************************************************/

/// @notice Called by the EigenPodManager to fulfill a partial withdrawal proof request
function fulfillPartialWithdrawalProofRequest(
IEigenPod.VerifiedPartialWithdrawalBatch calldata verifiedPartialWithdrawalBatch,
uint64 feeGwei,
address feeRecipient
) external onlyEigenPodManager {

require(verifiedPartialWithdrawalBatch.mostRecentWithdrawalTimestamp == mostRecentWithdrawalTimestamp, "EigenPod.fulfillPartialWithdrawalProofRequest: proven mostRecentWithdrawalTimestamp must match mostRecentWithdrawalTimestamp in the EigenPod");
require(mostRecentWithdrawalTimestamp < verifiedPartialWithdrawalBatch.endTimestamp, "EigenPod.fulfillPartialWithdrawalProofRequest: mostRecentWithdrawalTimestamp must precede endTimestamp");

require(verifiedPartialWithdrawalBatch.provenPartialWithdrawalSumGwei >= feeGwei, "EigenPod.fulfillPartialWithdrawalProofRequest: provenPartialWithdrawalSumGwei must be greater than the fee");

//update mostRecentWithdrawalTimestamp to currently proven endTimestamp
mostRecentWithdrawalTimestamp = verifiedPartialWithdrawalBatch.endTimestamp;

uint64 provenPartialWithdrawalSumGwei = verifiedPartialWithdrawalBatch.provenPartialWithdrawalSumGwei;
// subtract an partial withdrawals that may have been claimed via merkle proofs
if(provenPartialWithdrawalSumGwei > sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei){
Sidu28 marked this conversation as resolved.
Show resolved Hide resolved
if(sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei > 0){
provenPartialWithdrawalSumGwei -= sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei;
sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei = 0;
}

//Once sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei, we need to ensure that there is enough ETH in the pod to pay the fee
if(provenPartialWithdrawalSumGwei >= feeGwei){
provenPartialWithdrawalSumGwei -= feeGwei;
//send proof service their fee
AddressUpgradeable.sendValue(payable(feeRecipient), feeGwei);
}
if(provenPartialWithdrawalSumGwei > 0){
_sendETH_AsDelayedWithdrawal(podOwner, provenPartialWithdrawalSumGwei);
}

} else {
sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei -= provenPartialWithdrawalSumGwei;
}
}

/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
Expand Down Expand Up @@ -712,14 +754,18 @@
address recipient,
uint64 partialWithdrawalAmountGwei
) internal returns (VerifiedWithdrawal memory) {
require(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't it be enabled during the upgrade and paused via regular pausing method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump on this @Sidu28
I had the same questions.

!eigenPodManager.proofServiceEnabled(),
"EigenPod._processPartialWithdrawal: partial withdrawal merkle proofs are disabled"
);
emit PartialWithdrawalRedeemed(
validatorIndex,
withdrawalTimestamp,
recipient,
partialWithdrawalAmountGwei
);

sumOfPartialWithdrawalsClaimedGwei += partialWithdrawalAmountGwei;
sumOfPartialWithdrawalsClaimedViaMerkleProvenGwei += partialWithdrawalAmountGwei;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function should be removed, partial withdrawal processing in general outside of zk. if you think otherwise, can you write something for discussion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure where do you want me to write this? I think its pretty simple. In the event Risc0 goes down, we jjust have to make a very simple transaction (can be done via etherscan) vs an upgrade. I agree having non functional code in the contract isn't good but having Risc0 go down and being able to react really quick I think is important too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think leaving this code in reduces the trust in the system, at least in some cases.
I can see the potential for bugs in the interactions between this code and the new zk-batched proving code, but it does seem like we at least have to deal with most of these issues simply due to the upgrade happening at all.


// For partial withdrawals, the withdrawal amount is immediately sent to the pod owner
return
Expand Down Expand Up @@ -788,13 +834,15 @@
return _validatorPubkeyHashToInfo[pubkeyHash].status;
}


/// @notice Returns the validator status for a given validatorPubkey
function validatorStatus(bytes calldata validatorPubkey) external view returns (VALIDATOR_STATUS) {
bytes32 validatorPubkeyHash = _calculateValidatorPubkeyHash(validatorPubkey);
return _validatorPubkeyHashToInfo[validatorPubkeyHash].status;
}



/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
Expand Down
Loading
Loading